Evgenii Legotckoi
Evgenii LegotckoiOct. 7, 2017, 4:46 p.m.

SFML - Tutorial 001. Connecting the SFML library to a project on Qt

SFML is an object-oriented analog SDL. The library provides easy access to the systems of input-output, audio, data network, as well as to OpenGL functionality.

And what if, in addition, to add to this library also the Qt functionality, get into control the powerful capabilities of signals and slots, Qt resource files? What is required for this?

  1. Download the latest version of SFML source code from GitHub.
  2. Compile sources into the library
  3. Create a Qt project
  4. Connect the library to the project
  5. Write a widget that will be rendered directly through the SFML functionality

Build SFML

In general, you can download precompiled SFML libraries, but if you have a compiler that is different from the one that these libraries were assembled, then nothing will work. Therefore, we will not experiment, but immediately we will assemble a library with that compiler, which we will build the project.

SFML is represented by a project using the CMAKE build system, which is easily opened in Qt Creator.

When the project is open, we launch CMAKE, and then build the library itself, both in Debug mode and in Release mode, in order to have two sets of library libraries.

In the assembly directory there will be a lib directory, which will contain libraries of interest to us. We will add them to the project on Qt, in which SFML will be used.

The following libraries will be collected:

  • smfl-audio
  • sfml-graphics
  • sfml-network
  • sfml-window
  • sfml-system

Qt project

Next, create a new Qt project. Create a project using QWidget as the main application window. After the project is created, add the SFML directory to the project root, which will contain two directories: lib and include .

In the lib directory, you need to put all the compiled SFML libraries, both for the debug version and for the release version. And in the include directory, you need to put the files from the same include directory that is in the source project of the SFML library.

As a result, the structure of this directory in your Qt project will look like this.

After the library is prepared, it must be connected to the Qt project via a pro file.

To do this, we will write the following lines in the pro file:

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 - means the location of the pro project file, in which we register the project configuration and the connected files. That is, all the necessary library files will be searched relative to the project pro file.

Now let's start writing the project itself. Create an SFML widget that will fit inside the main widget of the application to show that you do not need to return the entire application window to use SFML.

The structure of the project will be as follows:

As you can see, there is a main widget of the Widget window, and there is also a qsqmlcanvas widget in which the SFML library will work. Also I added an image, through the resource file Qt ... Yes! It's possible! It is possible to use Qt resource files in the SFML library, you just need to write a few additional lines of code to properly extract images from the resource file.

Now let's start creating a project using Qt and SFML. It was decided to make an application in which the SFML widget will be twisted with the Qt logo.

Project profile and main.cpp

About connecting the library to the project was mentioned above, and the file main.cpp is not subject to any changes, so will not focus on them.

widget.h

In this project, I will not use the graphic designer, so manually write QGridLayout in the header file of the window where the SFML widget will be placed.

#ifndef WIDGET_H
#define WIDGET_H

#include <QWidget>
#include <QGridLayout>
#include "qsfmlcanvas.h" // Connecting the SFML widget

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

And in the constructor, create a QSFMLCanvas widget and set its location in the application window.

#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

Let's take a look at the contents of the widget class SFML widget.

#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 :
    // The method of initializing the widget
    void onInit();
    // Widget Content Update Method
    void onUpdate();
    // The method that returns the Qt rendering engine
    virtual QPaintEngine* paintEngine() const override;
    // The method of opening and closing the widget will be required for initial initialization
    virtual void showEvent(QShowEvent*) override;
    // The method of drawing, we need to redraw the widget
    virtual void paintEvent(QPaintEvent*) override;

private slots:
    // The slot in which the redraw event will be called
    void onTimeout();

private:
    QTimer m_timer;
    bool   m_initialized;

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

#endif // QSFMLCANVAS_H

The methods onInit() and onUpdate() are optional, and in fact you can call them whatever you want, it's important that you first need to add all the necessary objects and periodically update them.

But the paintEngine() method must be redefined necessarily, because we will not use the Qt rendering engine, then this method will have to return nullptr.

The showEvent() and paintEvent() methods will be used to initialize the SFML functional and initial rendering and update of object rendering. These are methods that are methods of the QWidget class from which the SFML class of the widget is inherited. Redrawing the SFML widget with these methods will be done within the Qt event infrastructure. What does it mean? The fact is that the paintEvent() method can be called by calling the repaint() method, repaint() calls the redraw event within the Qt application stack, so do not be surprised that the paintEvent() method does not directly call for a redraw.

You could also notice that this class uses multiple inheritance from two classes: QWidget and sf::RenderWindow .

We need inheritance from QWidget to get the ability to implement an SFML widget anywhere in the application interface, and also access the Qt signal functionality and slots, while inheritance from sf::RenderWindow is necessary to gain access to the functionality of this library, plus which is That it works directly with OpenGL .

qsqmlcanvas.cpp

#include "qsfmlcanvas.h"

#include <string>

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

QSFMLCanvas::QSFMLCanvas(QWidget *parent, uint frameTime) :
    QWidget(parent),
    // Initialize the base constructor of the SFML drawing window
    sf::RenderWindow(sf::VideoMode(800, 600), "OpenGL", sf::Style::Default, sf::ContextSettings(24)),
    m_initialized(false)
{
    // Make the settings to directly draw the image into the widget
    setAttribute(Qt::WA_PaintOnScreen);
    setAttribute(Qt::WA_OpaquePaintEvent);
    setAttribute(Qt::WA_NoSystemBackground);

    // Set strong focus to keyboard events
    setFocusPolicy(Qt::StrongFocus);

    // Set the time period for the redraw timer
    m_timer.setInterval(frameTime);
}

void QSFMLCanvas::onInit()
{
    // Loading images from Qt resource files
    // Quite an interesting point in the sense that 
    // SFML can not load an image from resources, like QPixmap or QImage
    QPixmap pixmap(":/QtCreator.png");  // But if you create a QPixmap object
    QByteArray bArray;                  // Create a byte array object
    QBuffer buffer(&bArray);            // Put it in the buffer
    buffer.open(QIODevice::WriteOnly);  // Save the image to this buffer, that is, to memory
    pixmap.save(&buffer, "PNG");     

    // And then pick up the raw data from the buffer, indicating the amount of data to be retrieved
    m_texture.loadFromMemory(buffer.data().data(), buffer.data().size());
    // This procedure will need to be done once when creating an SFML texture

    // Next, install the images in 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);     // Scale an image
    m_sprite.setOrigin(m_sprite.getTextureRect().width / 2, m_sprite.getTextureRect().height / 2); // Set the center of the image
    m_sprite.setPosition(200, 200);        // Set the image position
}

void QSFMLCanvas::onUpdate()
{
    // Clean the screen
    clear(sf::Color(128, 0, 0));

    // Rotate the image
    m_sprite.rotate(1);

    // Draw this image
    draw(m_sprite);
}

QPaintEngine* QSFMLCanvas::paintEngine() const
{
    // Return nullptr instead of the Qt rendering engine, so that Qt does not try to draw anything
    return nullptr;
}

void QSFMLCanvas::showEvent(QShowEvent*)
{
    // Initial initialization of the SFML widget
    if (!m_initialized)
    {
        // Create an SFML window for rendering with the id of the window in which the drawing will be done
        RenderWindow::create(winId());

        // Initializing drawing objects
        onInit();

        // Setting the timer to restart the widget's rendering
        connect(&m_timer, &QTimer::timeout, this, &QSFMLCanvas::onTimeout);
        m_timer.start();

        m_initialized = true;
    }
}

void QSFMLCanvas::paintEvent(QPaintEvent*)
{
    // Updating object rendering
    onUpdate();

    // Display the rendered window
    display();
}

void QSFMLCanvas::onTimeout()
{
    // Run the redrawing
    repaint();
}

Download project

ATTENTION: The project was created under KDE NEON 5.8.2 and is a sample, the correct start of which is not guaranteed under other OS, in particular you will need to build the SFML library under your target platform and with your compiler.

We recommend hosting TIMEWEB
We recommend hosting TIMEWEB
Stable hosting, on which the social network EVILEG is located. For projects on Django we recommend VDS hosting.

Do you like it? Share on social networks!

F
  • Oct. 10, 2017, 11:41 a.m.

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

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

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

F
  • Oct. 10, 2017, 12:18 p.m.

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

Evgenii Legotckoi
  • Oct. 10, 2017, 12:21 p.m.

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

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

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

Evgenii Legotckoi
  • Oct. 10, 2017, 12:26 p.m.

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

F
  • Oct. 10, 2017, 12:29 p.m.

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

Evgenii Legotckoi
  • Oct. 10, 2017, 12:32 p.m.

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

RenderWindow::create(static_cast<sf::WindowHandle>(winId()));
F
  • Oct. 10, 2017, 12:35 p.m.

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


F
  • Oct. 10, 2017, 12:36 p.m.

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

Evgenii Legotckoi
  • Oct. 10, 2017, 12:38 p.m.

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

F
  • Oct. 10, 2017, 12:38 p.m.

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

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

mingw


F
  • Oct. 10, 2017, 12:43 p.m.

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

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

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

Comments

Only authorized users can post comments.
Please, Log in or Sign up
г
  • ги
  • April 24, 2024, 1:51 a.m.

C++ - Test 005. Structures and Classes

  • Result:41points,
  • Rating points-8
l
  • laei
  • April 23, 2024, 7:19 p.m.

C ++ - Test 004. Pointers, Arrays and Loops

  • Result:10points,
  • Rating points-10
l
  • laei
  • April 23, 2024, 7:17 p.m.

C++ - Тест 003. Условия и циклы

  • Result:50points,
  • Rating points-4
Last comments
k
kmssrFeb. 9, 2024, 5:43 a.m.
Qt Linux - Lesson 001. Autorun Qt application under Linux как сделать автозапуск для флэтпака, который не даёт создавать файлы в ~/.config - вот это вопрос ))
Qt WinAPI - Lesson 007. Working with ICMP Ping in Qt Без строки #include <QRegularExpressionValidator> в заголовочном файле не работает валидатор.
EVA
EVADec. 25, 2023, 9:30 p.m.
Boost - static linking in CMake project under Windows Ошибка LNK1104 часто возникает, когда компоновщик не может найти или открыть файл библиотеки. В вашем случае, это файл libboost_locale-vc142-mt-gd-x64-1_74.lib из библиотеки Boost для C+…
J
JonnyJoDec. 25, 2023, 7:38 p.m.
Boost - static linking in CMake project under Windows Сделал всё по-как у вас, но выдаёт ошибку [build] LINK : fatal error LNK1104: не удается открыть файл "libboost_locale-vc142-mt-gd-x64-1_74.lib" Хоть убей, не могу понять в чём дел…
G
GvozdikDec. 19, 2023, 8:01 a.m.
Qt/C++ - Lesson 056. Connecting the Boost library in Qt for MinGW and MSVC compilers Для решения твой проблемы добавь в файл .pro строчку "LIBS += -lws2_32" она решит проблему , лично мне помогло.
Now discuss on the forum
G
GarApril 22, 2024, 3:46 p.m.
Clipboard Как скопировать окно целиком в clipb?
DA
Dr Gangil AcademicsApril 20, 2024, 5:45 p.m.
Unlock Your Aesthetic Potential: Explore MSC in Facial Aesthetics and Cosmetology in India Embark on a transformative journey with an msc in facial aesthetics and cosmetology in india . Delve into the intricate world of beauty and rejuvenation, guided by expert faculty and …
a
a_vlasovApril 14, 2024, 4:41 p.m.
Мобильное приложение на C++Qt и бэкенд к нему на Django Rest Framework Евгений, добрый день! Такой вопрос. Верно ли следующее утверждение: Любое Android-приложение, написанное на Java/Kotlin чисто теоретически (пусть и с большими трудностями) можно написать и на C+…
Павел Дорофеев
Павел ДорофеевApril 14, 2024, 12:35 p.m.
QTableWidget с 2 заголовками Вот тут есть кастомный QTableView с многорядностью проект поддерживается, обращайтесь
f
fastrexApril 4, 2024, 2:47 p.m.
Вернуть старое поведение QComboBox, не менять индекс при resetModel Добрый день! У нас много проектов в которых используется QComboBox, в версии 5.5.1, когда модель испускает сигнал resetModel, currentIndex не менялся. В версии 5.15 при resetModel происходит try…

Follow us in social networks