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?
- Download the latest version of SFML source code from GitHub.
- Compile sources into the library
- Create a Qt project
- Connect the library to the project
- 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(); }
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.
Здравствуйте!
Собирал под 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 просто это из-за размеров картинки была проблема
Спасибо за советы