Evgenii Legotckoi
Evgenii LegotckoiҚыр. 12, 2015, 12:12 Т.Қ.

Qt/C++ - 017-сабақ. QGraphicsScene немесе Qt тілінде графикамен жұмыс істеу жолы

Осы мақаладан бастап біз Qt графикалық кітапханаларын зерттей бастаймыз, дәлірек айтсақ, QGraphicsScene. Бұл класс 2D нысандарының үлкен санын басқаруға арналған функционалдылықты қамтамасыз етеді. QGraphicsScene QGraphicsView параметріне орнатылған.

Қолданбамызда іске асырылуы қажет функционалдылықты сипаттап көрейік:

  • QGraphicsView. қолданбасына графикалық көрініс қосу
  • Графикалық көріністе сызықтарды пайдаланып екі нысанды, атап айтқанда тіктөртбұрыш пен шаршыны салу.
  • QGraphicsView. өлшемін өзгертуге байланысты графикалық көріністің динамикалық өлшемін өзгерту.
  • Графикалық көріністің өлшеміне байланысты графикалық көріністегі объектілердің динамикалық өзгеруі.

Код Qt 5.4.1 негізінде QtCreator 3.3.1-де жазылған.

QGraphicsScene бағдарламасымен жұмыс істеуге арналған жоба құрылымы

QGraphicsScene бағдарламасымен жұмыс істеуге арналған жоба құрылымы Әдепкі жоба құрылымына тағы бір сынып MyGraphicView. қосылды.

Өйткені, QGraphicsScene -мен жұмыс істеуге ыңғайлы болу үшін QGraphicsView -дан мұрагер болатын және оның ішінде графикалық көрініспен және оның объектілерімен жұмыс істейтін класс құру туралы шешім қабылданды.


mainwindow.ui

Қолданбаның сыртқы түрі - бұл барлық терезеде орналасқан негізгі терезе және GridLayout. Сабақтардың соңында қосымша келесідей болады:

QGraphicsScene ## mainwindow.h көмегімен қолданбаның көрінісі

Бұл файлда біз тек MyGraphicsView. тақырып файлын қосамыз.

#ifndef MAINWINDOW\_H
#define MAINWINDOW\_H

#include <QMainWindow>

#include <mygraphicview.h>

namespace Ui {
class MainWindow;
}

class MainWindow : public QMainWindow
{
    Q\_OBJECT

public:
    explicit MainWindow(QWidget *parent = 0);
    ~MainWindow();

private:
    Ui::MainWindow  *ui;
    MyGraphicView   *myPicture;     // Наш кастомный виджет
};

#endif // MAINWINDOW\_H

mainwindow.cpp

Бұл файл да ерекше. Виджетті инициализациялаймыз және оны қолданба терезесінің GridLayout түріне қосамыз.

#include "mainwindow.h"
#include "ui\_mainwindow.h"

MainWindow::MainWindow(QWidget *parent) :
    QMainWindow(parent),
    ui(new Ui::MainWindow)
{
    ui->setupUi(this);

    /* Инициализируем виджет с графикой */
    myPicture   = new MyGraphicView();
    /* и добавляем его на слой */
    ui->graphicLayout->addWidget(myPicture);
}

MainWindow::~MainWindow()
{
    delete ui;
}

mygraphicview.h

Ал енді ең қызығы. Графикалық көрініспен тікелей жұмыс істеу, онда біз қолданба терезесінің өлшемін өзгерткен кезде қайта сызылуы қажет екі жаңа нысанды саламыз және сәйкесінше виджеттің өзі.

Графикалық көріністі динамикалық қайта сызу үшін, сондай-ақ негізгі терезені жасау кезінде терезенің ені мен биіктігі параметрлері бірден орнатылмайды, бірақ толық көрсетуден кейін, сондықтан виджет мазмұнын көрсету үшін біраз уақыт кідірісі қажет болады. құрамында болатын виджеттің ені мен биіктігі үшін дұрыс мәндерді алыңыз QGraphicsScene. Ол үшін [QTimer] сыныбын қолданамыз(https://evileg.com/post/72 /) , оның толып кетуіне біз графикалық көріністің мазмұны сызылған және оның өлшемі реттелетін SLOT деп атаймыз.

#ifndef MYGRAPHICVIEW\_H
#define MYGRAPHICVIEW\_H

#include <QWidget>
#include <QGraphicsView>
#include <QGraphicsScene>
#include <QGraphicsItemGroup>
#include <QTimer>


// Расширяем класс QGraphicsView
class MyGraphicView : public QGraphicsView
{
    Q\_OBJECT
public:
    explicit MyGraphicView(QWidget *parent = 0);
    ~MyGraphicView();

signals:

private slots:
    void slotAlarmTimer();  /* слот для обработчика переполнения таймера
                             * в нём будет производиться перерисовка
                             * виджета
                             * */

private:
    QGraphicsScene      *scene;     // Объявляем сцену для отрисовки
    QGraphicsItemGroup  *group\_1;   // Объявляем первую группу элементов
    QGraphicsItemGroup  *group\_2;   // Объявляем вторую группу элементов

    /* Таймер для задержки отрисовки.
     * Дело в том, что при создании окна и виджета
     * необходимо некоторое время, чтобы родительский слой
     * развернулся, чтобы принимать от него адекватные параметры
     * ширины и высоты
     * */
    QTimer              *timer;

private:
    /* Перегружаем событие изменения размера окна,
     * чтобы перехватывать его
     * */
    void resizeEvent(QResizeEvent *event);
    /* Метод для удаления всех элементов
     * из группы элементов
     * */
    void deleteItemsFromGroup(QGraphicsItemGroup *group\_1);
};

#endif // MYGRAPHICVIEW\_H

mygraphicview.cpp

QGraphicsScene ішіндегі нысандарды қайта сызу үшін дәл осы нысандарды жою қажет, сондықтан жұмыс ыңғайлы болу үшін осы объектілердің элементтері топтастырылады және топтың барлық элементтерін жою әдісі жазылады. Бұл бірнеше элементтерден тұратын бірнеше нысанның ішінен тек бір объектіні қайта салу қажет болса ыңғайлы.

#include "mygraphicview.h"

MyGraphicView::MyGraphicView(QWidget *parent)
    : QGraphicsView(parent)
{

    /* Немного поднастроим отображение виджета и его содержимого */
    this->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff); // Отключим скроллбар по горизонтали
    this->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff);   // Отключим скроллбар по вертикали
    this->setAlignment(Qt::AlignCenter);                        // Делаем привязку содержимого к центру
    this->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);    // Растягиваем содержимое по виджету

    /* Также зададим минимальные размеры виджета
     * */
    this->setMinimumHeight(100);
    this->setMinimumWidth(100);

    scene = new QGraphicsScene();   // Инициализируем сцену для отрисовки
    this->setScene(scene);          // Устанавливаем сцену в виджет

    group\_1 = new QGraphicsItemGroup(); // Инициализируем первую группу элементов
    group\_2 = new QGraphicsItemGroup(); // Инициализируем вторую группу элементов

    scene->addItem(group\_1);            // Добавляем первую группу в сцену
    scene->addItem(group\_2);            // Добавляем вторую группу в сцену

    timer = new QTimer();               // Инициализируем Таймер
    timer->setSingleShot(true);
    // Подключаем СЛОТ для отрисовки к таймеру
    connect(timer, SIGNAL(timeout()), this, SLOT(slotAlarmTimer()));
    timer->start(50);                   // Стартуем таймер на 50 миллисекунд
}

MyGraphicView::~MyGraphicView()
{

}

void MyGraphicView::slotAlarmTimer()
{
    /* Удаляем все элементы со сцены,
     * если они есть перед новой отрисовкой
     * */
    this->deleteItemsFromGroup(group\_1);
    this->deleteItemsFromGroup(group\_2);

    int width = this->width();      // определяем ширину нашего виджета
    int height = this->height();    // определяем высоту нашего виджета

    /* Устанавливаем размер сцены по размеру виджета
     * Первая координата - это левый верхний угол,
     * а Вторая - это правый нижний угол
     * */
    scene->setSceneRect(0,0,width,height);

    /* Приступаем к отрисовке произвольной картинки
     * */
    QPen penBlack(Qt::black); // Задаём чёрную кисть
    QPen penRed(Qt::red);   // Задаём красную кисть

    /* Нарисуем черный прямоугольник
     * */
    group\_1->addToGroup(scene->addLine(20,20, width - 20, 20, penBlack));
    group\_1->addToGroup(scene->addLine(width - 20, 20, width - 20, height -20, penBlack));
    group\_1->addToGroup(scene->addLine(width - 20, height -20, 20, height -20, penBlack));
    group\_1->addToGroup(scene->addLine(20, height -20, 20, 20, penBlack));

    /* Нарисуем красный квадрат
     * */
    int sideOfSquare = (height > width) ? (width - 60) : (height - 60);
    int centerOfWidget\_X = width/2;
    int centerOfWidget\_Y = height/2;

    group\_2->addToGroup(scene->addLine(centerOfWidget\_X - (sideOfSquare/2),
                                       centerOfWidget\_Y - (sideOfSquare/2),
                                       centerOfWidget\_X + (sideOfSquare/2),
                                       centerOfWidget\_Y - (sideOfSquare/2),
                                       penRed));

    group\_2->addToGroup(scene->addLine(centerOfWidget\_X + (sideOfSquare/2),
                                       centerOfWidget\_Y - (sideOfSquare/2),
                                       centerOfWidget\_X + (sideOfSquare/2),
                                       centerOfWidget\_Y + (sideOfSquare/2),
                                       penRed));

    group\_2->addToGroup(scene->addLine(centerOfWidget\_X + (sideOfSquare/2),
                                       centerOfWidget\_Y + (sideOfSquare/2),
                                       centerOfWidget\_X - (sideOfSquare/2),
                                       centerOfWidget\_Y + (sideOfSquare/2),
                                       penRed));

    group\_2->addToGroup(scene->addLine(centerOfWidget\_X - (sideOfSquare/2),
                                       centerOfWidget\_Y + (sideOfSquare/2),
                                       centerOfWidget\_X - (sideOfSquare/2),
                                       centerOfWidget\_Y - (sideOfSquare/2),
                                       penRed));
}

/* Этим методом перехватываем событие изменения размера виджет
 * */
void MyGraphicView::resizeEvent(QResizeEvent *event)
{
    timer->start(50);   // Как только событие произошло стартуем таймер для отрисовки
    QGraphicsView::resizeEvent(event);  // Запускаем событие родителького класса
}


/* Метод для удаления всех элементов из группы
 * */
void MyGraphicView::deleteItemsFromGroup(QGraphicsItemGroup *group)
{
    /* Перебираем все элементы сцены, и если они принадлежат группе,
     * переданной в метод, то удаляем их
     * */
    foreach( QGraphicsItem *item, scene->items(group->boundingRect())) {
       if(item->group() == group ) {
          delete item;
       }
    }
}

Барлығы

Өтінім нәтижесі келесі бейнеде 5:27-де көрсетілген. Осы уақытқа дейін бейнеде жобаның түсіндірмесі бар.

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

Ол саған ұнайды ма? Әлеуметтік желілерде бөлісіңіз!

D
  • Наурыз 28, 2017, 8:42 Т.Ж.

Добрый день, Евгений! Спасибо за ваши уроки и за ваш исходный код к ним. Только небольшой вопрос - а где исходный код главной функции main()? без нее ничего не запускается. Напишите, пожалуйста, ответ, ну или, исходный код функции main(). Заранее спасибо.

Evgenii Legotckoi
  • Наурыз 28, 2017, 8:55 Т.Ж.

Добрый день, Doug.
Я обычно прикладываю код функции main тогда, когда она отличается от файла в проекте созданном по умолчанию.

Например здесь, используется класс mainwindow для окна приложения. Он будет выглядеть так, если создадите в Qt Creator приложение на Qt, у которого базовым классом будет класс QMainWindow. 

#include "mainwindow.h"
#include <QApplication>

int main(int argc, char *argv[])
{
    QApplication a(argc, argv);

    MainWindow w;
    w.show();

    return a.exec();
}
D
  • Наурыз 28, 2017, 9:09 Т.Ж.

Спасибо, Евгений!! А файл main.cpp во всех других уроках останется таким же? ( я имею ввиду уроки, где вы рассказываете о работе с 2d и QGraphicsScene ? ... и, если есть какие-то изменения, не могли бы вы тоже добавить в другие уроки файл main.cpp c главной функцией main()? Еще раз спасибо.

Evgenii Legotckoi
  • Наурыз 28, 2017, 9:17 Т.Ж.

Там может быть разница в том, что класс окна может быть Widget вместо MainWindow . Но это должно быть очевидно из класса главного окна приложения. Поэтому просто вместо MainWindow пишите Widget . Код этой функции приводится в том случае, если он отличается от кода по умолчанию при создании нового проекта.

D
  • Наурыз 28, 2017, 10:44 Т.Ж.

Понял. Хорошо.Спасибо. Удачи Вам!

D
  • Наурыз 29, 2017, 12:52 Т.Ж.

Добрый день! Можно такой вопрос - как организовать перемещение / поворот фигур ПРАВОЙ кнопкой мыши? И как сделать, так чтобы фигуру в фигуре (например, квадрат в треугольнике), можно было перемещать / вращать как единое целое (монолит)? Спасибо.

Evgenii Legotckoi
  • Наурыз 29, 2017, 1:11 Т.Ж.

Что касается перемещения, то можно организовать либо как сделано в этой статье

Либо можно воспользоваться флагами:

  1. ItemIsMovable - включает возможность перемещения
  2. ItemIsSelectable - включает возможность выделения объектов

Флаги применяются непосредственно к QGraphicsItem . Можно сразу в конструкторе настраивать.

setFlags(QGraphicsItem::ItemIsMovable|QGraphicsItem::ItemIsSelectable);

Что касается поворота объектов, то встроенными средствами Qt это не реализуется, в том смысле, что там нет волшебного флага наподобие ItemIsMovable , который просто возьмёт и включит данную возможность. Я как-то реализовывал подобное в одном тестовом задании, но нужно искать исходники. Там много строчек написано. Поэтому ответить сходу затрудняюсь.

P/S/ Этот ваш последний вопрос имеет косвенное отношение к статье, поэтому не могли бы Вы задавать такие вопросы сразу на форуме ? Чтобы была уже отдельная ветка обсуждения.

D
  • Наурыз 29, 2017, 1:27 Т.Ж.

Спасибо, Евгений за Ваш такой оперативный ответ. Не знал, что есть форум. Спасибо за ссылку.

МК
  • Қыр. 27, 2017, 1:12 Т.Қ.

Благодарю за уроки!
Сделал вроде бы всё как написано, но при изменении размеров окна не происходит изменения размеров виджета, и соответственно геометрических фигур. В чём причина, подскажите.
Кстати, можно сделать подключение сигнала к слоту в новом синтаксисе:

// Подключаем СЛОТ для отрисовки к таймеру
//connect(timer, SIGNAL(timeout()), this, SLOT(slotAlarmTimer()));
connect(timer, &QTimer::timeout, this, &MyGraphicView::slotAlarmTimer);

Нашёл ответ на просторах интернета:
В Дизайнере ПКМ по MainWindow: Компоновка - Скомпоновать по сетке.
Нужно писать о таких простых, но совсем не очевидных для новичка вещах.
ЗЫ: увидел у вас 24-ый урок про сигналы и слоты, так что вторая часть предыдущего комментария также снимается ))

Evgenii Legotckoi
  • Қыр. 27, 2017, 1:39 Т.Қ.

Добрый день!
О всех очевидных вещах не напишешь. Молодца, что нашли решение самостоятельно.

a
  • Мамыр 10, 2018, 1:34 Т.Ж.

Евгений, добрый день!

А где можно исходники найти?

a
  • Мамыр 10, 2018, 3:25 Т.Ж.

Взял код отсюда. Приделал graphicLayout. Всё, заработало, спасибо.

Правда, так и не понятно зачем этот таймер, зачем эта задержка при отрисовки. Почему бы сразу мгновенно не отрисовывать?
Evgenii Legotckoi
  • Мамыр 10, 2018, 3:47 Т.Ж.

Я уже не помню какая там проблема была.

r
  • Қыр. 25, 2018, 6:37 Т.Ж.

В статье написано, что таймер сработает один раз. Но это не так. Было бы хорошо добавить

timer->setSingleShot(true);

После инициализации таймера.

Evgenii Legotckoi
  • Қыр. 25, 2018, 6:43 Т.Ж.

Прямо так не написано.

Хотя соглашусь, что в качестве улучшения вызов данного метода здесь к месту.

G.
  • Қаң. 31, 2019, 7:48 Т.Ж.

Где вызывается наш метод resizeEvent() ?

Evgenii Legotckoi
  • Қаң. 31, 2019, 8:01 Т.Ж.

Он вызывается в очереди событий в рамках обработки событий взаимодействия с окном. Это всё крутится под капотом Qt. У виджетов есть методы Event, они вызываются в зависимости от срабатывания событий. Если хотите знать, где конкретно, то лезьте в исходники Qt.

G.
  • Қаң. 31, 2019, 10:38 Т.Ж.

Спасибо большое! Разобрался.

МА
  • Ақп. 26, 2019, 7:07 Т.Ж.
  • (өңделген)

В файле "mainwindow.cpp" есть строчка:
ui->graphicLayout->addWidget(myPicture);

Возникает вопрос: что такое "graphicLayout"? Где/Как его найти/создать?

upd
Уже разобрался. Т.к. работаю с C++ всего пару часов, не сразу понял, что это просто объект QGridLayout.

Evgenii Legotckoi
  • Ақп. 26, 2019, 7:27 Т.Ж.
  • (өңделген)

Объект был создан через графический дизайнер. ui создаётся в графическом дизайнере.

Пікірлер

Тек рұқсаты бар пайдаланушылар ғана пікір қалдыра алады.
Кіріңіз немесе Тіркеліңіз
OI
  • Ora Iro
  • Жел. 24, 2024, 6:38 Т.Ж.

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

  • Нәтиже:40ұпай,
  • Бағалау ұпайлары-8
AD

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

  • Нәтиже:50ұпай,
  • Бағалау ұпайлары-4
m
  • molni99
  • Қаз. 26, 2024, 1:37 Т.Ж.

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

  • Нәтиже:80ұпай,
  • Бағалау ұпайлары4
Соңғы пікірлер
ИМ
Игорь МаксимовҚар. 22, 2024, 11:51 Т.Ж.
Django - Оқулық 017. Теңшелген Django кіру беті Добрый вечер Евгений! Я сделал себе авторизацию аналогичную вашей, все работает, кроме возврата к предидущей странице. Редеректит всегда на главную, хотя в логах сервера вижу запросы на правильн…
Evgenii Legotckoi
Evgenii LegotckoiҚаз. 31, 2024, 2:37 Т.Қ.
Django - Сабақ 064. Python Markdown кеңейтімін қалай жазуға болады Добрый день. Да, можно. Либо через такие же плагины, либо с постобработкой через python библиотеку Beautiful Soup
A
ALO1ZEҚаз. 19, 2024, 8:19 Т.Ж.
Qt Creator көмегімен fb3 файл оқу құралы Подскажите как это запустить? Я не шарю в программировании и кодинге. Скачал и установаил Qt, но куча ошибок выдается и не запустить. А очень надо fb3 переконвертировать в html
ИМ
Игорь МаксимовҚаз. 5, 2024, 7:51 Т.Ж.
Django - Сабақ 064. Python Markdown кеңейтімін қалай жазуға болады Приветствую Евгений! У меня вопрос. Можно ли вставлять свои классы в разметку редактора markdown? Допустим имея стандартную разметку: <ul> <li></li> <li></l…
d
dblas5Шілде 5, 2024, 11:02 Т.Ж.
QML - Сабақ 016. SQLite деректер қоры және онымен QML Qt-та жұмыс істеу Здравствуйте, возникает такая проблема (я новичок): ApplicationWindow неизвестный элемент. (М300) для TextField и Button аналогично. Могу предположить, что из-за более новой верси…
Енді форумда талқылаңыз
Evgenii Legotckoi
Evgenii LegotckoiМаусым 24, 2024, 3:11 Т.Қ.
добавить qlineseries в функции Я тут. Работы оень много. Отправил его в бан.
t
tonypeachey1Қар. 15, 2024, 6:04 Т.Ж.
google domain [url=https://google.com/]domain[/url] domain [http://www.example.com link title]
NSProject
NSProjectМаусым 4, 2022, 3:49 Т.Ж.
Всё ещё разбираюсь с кешем. В следствии прочтения данной статьи. Я принял для себя решение сделать кеширование свойств менеджера модели LikeDislike. И так как установка evileg_core для меня не была возможна, ибо он писался…
9
9AnonimҚаз. 25, 2024, 9:10 Т.Ж.
Машина тьюринга // Начальное состояние 0 0, ,<,1 // Переход в состояние 1 при пустом символе 0,0,>,0 // Остаемся в состоянии 0, двигаясь вправо при встрече 0 0,1,>…

Бізді әлеуметтік желілерде бақылаңыз