SFML представляет собой объектно-ориентированный аналог SDL. Библиотека предоставляет простой доступ к системам ввода вывода, аудио, сети передачи данных, а также к функционалу OpenGL.
А что если дополнительно прикрутить к этой библиотеке ещё и функционал Qt, получить в управление мощные возможности сигналов и слотов, ресурсных файлов Qt? Что для этого потребуется?
- Скачать с GitHub последнюю версию исходников SFML .
- Скомпилировать исходники в библиотеку
- Создать проект Qt
- Подключить библиотеку в проект
- Написать виджет, который будет рендериться напрямую через функционал SFML
Сборка SFML
Вообще, можно скачать предкомпилированные библиотеки SFML, но если у Вас будет компилятор, отличный от того, которым были собраны эти библиотеки, то соответственно ничего не заработает. Поэтому не будем экспериментировать, а сразу соберём библиотеку с тем компилтором, которым будем производить сборку проекта.
SFML представлен проектом с использованием системы сборки CMAKE, который без проблем открывается в Qt Creator.
Когда проект открыт, производим запуск CMAKE, а потом сборку самой библиотеки, как в режим Debug, так и в режим Release, чтобы иметь два комплекта бибилиотек.
В каталоге сборки будет каталог lib, в котором будет находится интересующие нас библиотеки. Их Мы добавим в проект на Qt, в котором будет использоваться SFML.
Будут собраны следующие библиотеки:
- smfl-audio
- sfml-graphics
- sfml-network
- sfml-window
- sfml-system
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 под вашу целевую платформу и с вашим компилятором.
Здравствуйте!
Собирал под Windows 7. Компилировал с тем же компилятором, с которым работает Qt
Но если эту строку закомментировать, то приложение запускается и создается 2 окна(никакого изображения конечно же нет). OpenGl пустое, а второе с черными квадратными областями
Два окна из-за того, что хендлер не был передан для отрисовки.
Нет, так есть тож не хочет
Более полный текст ошибки тогда давайте. Наверняка там есть более подробная информация. Пока у меня идей нет.
Когда следую вашему методу с cast пишет "invalid conversion from long unsigned int to sf::WindowHandle {aka HWND__*}' [fpermissive]"
Ну тогда такой каст
Теперь пишет тоже самое просто вместо unsigned long int - "WId{aka unsigned int} "
а вот так все работает RenderWindow::create(sf::WindowHandle(winId()));
Забавно. Какой компилятор использовали?
открывается только 1 окно но картинка не грузится
mingw
А нет все отлично работает и с jpg просто это из-за размеров картинки была проблема
Спасибо за советы