Evgenii Legotckoi
Evgenii Legotckoi07 жовтня 2017 р. 16:46

SFML - Підручник 001. Підключення бібліотеки SFML до проекту на Qt

SFML є об'єктно-орієнтованим аналогом SDL. Бібліотека надає простий доступ до систем введення виводу, аудіо, мережі передачі даних, а також до функціоналу OpenGL.

А якщо додатково прикрутити до цієї бібліотеки ще й функціонал Qt, отримати в управління потужні можливості сигналів і слотів, ресурсних файлів Qt? Що для цього потрібно?

  1. Завантажити з GitHub останню версію вихідних файлів SFML .
  2. Скомпілювати вихідники до бібліотеки
  3. Створити проект Qt
  4. Підключити бібліотеку до проекту
  5. Написати віджет, який рендеруватиметься безпосередньо через функціонал SFML

Складання SFML

Взагалі, можна скачати передкомпільовані бібліотеки SFML, але якщо у Вас буде компілятор, відмінний від того, яким було зібрано ці бібліотеки, то нічого не запрацює. Тому не експериментуватимемо, а відразу зберемо бібліотеку з тим компілтором, яким будемо проводити складання проекту.

SFML представлений проектом із використанням системи складання CMAKE, який без проблем відкривається у Qt Creator.

Коли проект відкритий, здійснюємо запуск CMAKE, а потім складання самої бібліотеки, як у режим Debug, так і в режим Release, щоб мати два комплекти бібіліотек.

У каталозі збірки буде каталог lib, в якому буде знаходиться цікаві для нас бібліотеки. Їх Ми додамо до проекту на Qt, у якому буде використовуватися SFML.

Будуть зібрані такі бібліотеки:

  • smfl-аудіо
  • sfml-графіка
  • sfml-мережа
  • sfml-вікно
  • sfml-система

Qt проект

Далі створюємо новий Qt-проект. Створимо проект з використанням QWidget як основне вікно програми. Після того, як проект створений, додайте в корінь проекту каталог SFML, в якому будуть міститися дві директорії: lib і include.

У каталог lib необхідно помістити всі скомпіловані бібілотеки SFML як для debug версії, так і для release версії. А в каталог include необхідно помістити файли з того самого каталогу include, який знаходиться у проекті вихідних записів бібліотеки SFML.

В результаті структура даного каталогу у Qt проекті буде виглядати наступним чином.

Після того, як бібліотека підготовлена, її необхідно підключити до Qt проекту через pro файл.

Для цього пропишемо наступні рядки у pro файлі:

win32:CONFIG(release, debug|release): LIBS += -L$$PWD/SFML/lib/ -lsfml-audio -lsfml-graphics -lsfml-network -lsfml-window -lsfml-system
else:win32:CONFIG(debug, debug|release): LIBS += -L$$PWD/SFML/lib/ -lsfml-audio-d -lsfml-graphics-d -lsfml-network-d -lsfml-window-d -lsfml-system-d
else:unix: LIBS += -L$$PWD/SFML/lib/ -lsfml-audio -lsfml-graphics -lsfml-network -lsfml-window -lsfml-system

INCLUDEPATH += $$PWD/SFML/include
DEPENDPATH += $$PWD/SFML/include

$$PWD - означає місцезнаходження pro файлу проекту, в якому ми прописуємо конфігурацію проекту та файли, що підключаються. Тобто всі необхідні файли бібліотеки будемо шукати щодо pro-файлу проекту.

А тепер почнемо написання самого проекту. Створимо SFML віджет, який буде розміщуватися всередині основного віджету програми, щоб показати, що для використання SFML не обов'язково віддавати все вікно програми.

Структура проекту буде наступною:

Як бачите, тут є основний віджет вікна Widget, а також є віджет qsqmlcanvas, в якому буде здійснюватися робота бібліотеки SFML. Також я додав зображення через ресурсний файл Qt... Так! Це можливо! Можливо використовувати ресурсні файли Qt у бібліотеці SFML, просто потрібно написати кілька додаткових рядків коду для правильного вилучення зображень із ресурсного файлу.

А тепер приступимо до створення проекту з використанням Qt та SFML. Було вирішено зробити додаток, у SFML віджеті якого буде крутитися логотип Qt.

Профайл проекту та main.cpp

Про підключення бібліотеки до проекту було сказано вище, а файл main.cpp не зазнає жодних змін, тому не буде акцентувати увагу на них.

widget.h

У цьому проекті я не використовуватиму графічний дизайнер, тому вручну пропишемо QGridLayout в заголовному файлі вікна, в якому розміщуватиметься SFML віджет.

#ifndef WIDGET_H
#define WIDGET_H

#include <QWidget>
#include <QGridLayout>
#include "qsfmlcanvas.h" // Подключение виджета SFML

class Widget : public QWidget
{
    Q_OBJECT

public:
    explicit Widget(QWidget* parent = nullptr);
    ~Widget();

private:
    QSFMLCanvas* m_sfmlWidget;
    QGridLayout* m_gridLayout;
};

#endif // WIDGET_H

widget.cpp

А в конструкторі створимо QSFMLCanvas віджет та встановимо його розміщення у вікні програми.

#include "widget.h"

Widget::Widget(QWidget *parent) :
    QWidget(parent)
{
    resize(640, 480);
    m_sfmlWidget = new QSFMLCanvas(this);
    m_gridLayout = new QGridLayout(this);
    m_gridLayout->addWidget(m_sfmlWidget, 0, 0);
}

Widget::~Widget()
{

}

qsfmlcanvas.h

Розберемо вміст заголовного класу SFML віджету.

#ifndef QSFMLCANVAS_H
#define QSFMLCANVAS_H

#include <QWidget>
#include <QTimer>
#include <SFML/Graphics.hpp>

class QSFMLCanvas : public QWidget, public sf::RenderWindow
{
    Q_OBJECT
public:
    explicit QSFMLCanvas(QWidget* parent = nullptr, uint frameTime = 50);
    virtual ~QSFMLCanvas() {}

private :
    // Метод первоначальной инициализации виджета 
    void onInit();
    // Метод обновления содержимого виджета
    void onUpdate();
    // Метод, возвращающий движок отрисовки Qt
    virtual QPaintEngine* paintEngine() const override;
    // Метод событий открытия и закрытия виджета, понадобится для первоначальной инициализации
    virtual void showEvent(QShowEvent*) override;
    // Метод отрисовки, понадобится нам для перерисовки виджета
    virtual void paintEvent(QPaintEvent*) override;

private slots:
    // Слот, в котором будет вызываться событие перерисовки
    void onTimeout();

private:
    QTimer m_timer;
    bool   m_initialized;

    sf::Texture  m_texture;
    sf::Sprite   m_sprite;
};

#endif // QSFMLCANVAS_H

Методи onInit() та onUpdate() є опціональними і по суті ви можете назвати їх як захочете, важливо те, що потрібно спочатку додати всі необхідні об'єкти та періодично оновлювати їх.

А ось метод paintEngine() необхідно перевизначити обов'язково, оскільки ми не будемо використовувати движок змальовування Qt, то цей метод повинен буде повертати nullptr.

Методи showEvent() та paintEvent() будуть використовуватися для ініціалізації функціоналу SFML та початкового відтворення та оновлення відображення об'єктів. Це методи, які є методами класу QWidget , від якого успадковано клас SFML віджету. Перемальовка SFML віджету завдяки цим методам будемо проводитися в рамках інфраструктури подій Qt. Що це означає? Справа в тому, що метод paintEvent() може бути викликаний викликом методу repaint() , repaint() викликає подію перемальовки в рамках стека викликів Qt програми, тому не варто дивуватися, що для перемалювання не здійснюється прямого виклику методу paintEvent().

Також ви могли помітити, що в даному класі використовується множинне успадкування від двох класів: QWidget та sf::RenderWindow.

Наслідування від QWidget нам необхідно, щоб отримати можливість впровадження SFML віджету в будь-яке місце інтерфейсу програми, а також отримати доступ до функціоналу сигналів і слотів Qt, тоді як успадкування від sf::RenderWindow необхідно для отримання доступу до функціоналу даної бібілотеки плюс якої полягає в тому, що вона безпосередньо працює з OpenGL.

qsqmlcanvas.cpp

#include "qsfmlcanvas.h"

#include <string>

#include <QPixmap>
#include <QByteArray>
#include <QBuffer>

QSFMLCanvas::QSFMLCanvas(QWidget *parent, uint frameTime) :
    QWidget(parent),
    // Инициализируем базовый конструктор окна отрисовки SFML
    sf::RenderWindow(sf::VideoMode(800, 600), "OpenGL", sf::Style::Default, sf::ContextSettings(24)),
    m_initialized(false)
{
    // Произведём настройку для непосредственной отрисовки изображения в виджет
    setAttribute(Qt::WA_PaintOnScreen);
    setAttribute(Qt::WA_OpaquePaintEvent);
    setAttribute(Qt::WA_NoSystemBackground);

    // Установим строгую фокусировку для включения событий клавиатуры
    setFocusPolicy(Qt::StrongFocus);

    // Настроим период сработки таймера перерисовки
    m_timer.setInterval(frameTime);
}

void QSFMLCanvas::onInit()
{
    // Загрузка изображения из ресурсных файлов Qt
    // Довольно интересный момент в том плане, что
    // SFML не может загрузить изображение из ресурсов, как это делают QPixmap или QImage
    QPixmap pixmap(":/QtCreator.png");  // Но если создать объект QPixmap
    QByteArray bArray;                  // Создать объект массива байтов
    QBuffer buffer(&bArray);            // Поместить его в буфер
    buffer.open(QIODevice::WriteOnly);  // Сохранить изображение в этот буфер, то есть в память
    pixmap.save(&buffer, "PNG");     

    // А потом забрать сырые данные из буффера, с указанием количества забираемых данных
    m_texture.loadFromMemory(buffer.data().data(), buffer.data().size());
    // Эту процедуру понадобится произвести один раз при создании текстуры SFML

    // Далее устанавливаем изображения в sprite
    m_sprite.setTexture(m_texture);
    float xScale = 200. / m_sprite.getTextureRect().width;
    float yScale = 200. / m_sprite.getTextureRect().height;
    m_sprite.setScale(xScale, yScale);     // Масштабируем изображение
    m_sprite.setOrigin(m_sprite.getTextureRect().width / 2, m_sprite.getTextureRect().height / 2); // Устанавливаем центр изображения
    m_sprite.setPosition(200, 200);        // Устанавливаем позицию изображения
}

void QSFMLCanvas::onUpdate()
{
    // Очищаем экран
    clear(sf::Color(128, 0, 0));

    // Поворачиваем изображение
    m_sprite.rotate(1);

    // Отрисовываем это изображение
    draw(m_sprite);
}

QPaintEngine* QSFMLCanvas::paintEngine() const
{
    // Возвращаем nullptr вместо движка отрисовки Qt, чтобы Qt не пытался что либо рисовать сам
    return nullptr;
}

void QSFMLCanvas::showEvent(QShowEvent*)
{
    // Первичная инициализация виджета SFML
    if (!m_initialized)
    {
        // Создаём SFML окно для отрисовки с указанием Id окна, в котором будет производиться отрисовка
        RenderWindow::create(winId());

        // Инициализация объектов отрисовки
        onInit();

        // Настройка таймера для перезапуска отрисовки виджета
        connect(&m_timer, &QTimer::timeout, this, &QSFMLCanvas::onTimeout);
        m_timer.start();

        m_initialized = true;
    }
}

void QSFMLCanvas::paintEvent(QPaintEvent*)
{
    // Обновление отрисовки объектов
    onUpdate();

    // Отображение отрисованного окна
    display();
}

void QSFMLCanvas::onTimeout()
{
    // Запуск перерисовки
    repaint();
}

Завантажити проект

УВАГА: Проект створювався під KDE NEON 5.8.2 і є зразком, коректний запуск якого не гарантований під іншими ОС, зокрема Вам потрібна збірка бібліотеки SFML під вашу цільову платформу та з вашим компілятором.

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

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

F
  • 10 жовтня 2017 р. 11:41

Здравствуйте!

При сборке проекта выскакивает ошибка в файле qsfmlcanvas.cpp - строка  RenderWindow::create(winId());  метода showEvent пишет invalid conversion
Evgenii Legotckoi
  • 10 жовтня 2017 р. 12:14
День добрый!
Под какой ОС собирали? Компилировали SFML библиотеки под свой компилятор?
F
  • 10 жовтня 2017 р. 12:16

Собирал под Windows 7. Компилировал с тем же компилятором, с которым работает Qt

F
  • 10 жовтня 2017 р. 12:18

Но если эту строку закомментировать, то приложение запускается и создается 2 окна(никакого изображения конечно же нет). OpenGl пустое, а второе с черными квадратными  областями

Evgenii Legotckoi
  • 10 жовтня 2017 р. 12:21

Два окна из-за того, что хендлер не был передан для отрисовки.

Возможно, нужно поиграться со static_cast , чтобы получить правильное преобразование хендлера WId в то, что слопает метод RenderWindow::create().
Попробуйте так кастовать:
RenderWindow::create(static_cast<unsigned long>(winId()));
F
  • 10 жовтня 2017 р. 12:24

Нет, так есть тож не хочет

Evgenii Legotckoi
  • 10 жовтня 2017 р. 12:26

Более полный текст ошибки тогда давайте. Наверняка там есть более подробная информация. Пока у меня идей нет.

F
  • 10 жовтня 2017 р. 12:29

Когда следую вашему методу с cast пишет "invalid conversion from long unsigned int to sf::WindowHandle {aka HWND__*}' [fpermissive]"

Evgenii Legotckoi
  • 10 жовтня 2017 р. 12:32

Ну тогда такой каст

RenderWindow::create(static_cast<sf::WindowHandle>(winId()));
F
  • 10 жовтня 2017 р. 12:35

Теперь пишет тоже самое просто вместо unsigned long int - "WId{aka unsigned int} "


F
  • 10 жовтня 2017 р. 12:36

а вот так все работает  RenderWindow::create(sf::WindowHandle(winId()));

Evgenii Legotckoi
  • 10 жовтня 2017 р. 12:38

Забавно. Какой компилятор использовали?

F
  • 10 жовтня 2017 р. 12:38

открывается только 1 окно но картинка не грузится

хотя в ресурсах проекта она есть или тип PNG обязателен
F
  • 10 жовтня 2017 р. 12:39

mingw


F
  • 10 жовтня 2017 р. 12:43

А нет все отлично работает и с jpg просто это из-за размеров картинки была проблема

float xScale = 800. / m_sprite.getTextureRect().width;
float yScale = 800. / m_sprite.getTextureRect().height;
Здесь подставил свои и все норм
F
  • 10 жовтня 2017 р. 12:43

Спасибо за советы

Коментарі

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,>…

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