Evgenii Legotckoi
Evgenii Legotckoi7. Oktober 2017 16:46

SFML - Tutorial 001. Die SFML-Bibliothek mit einem Projekt auf Qt . verbinden

SFML ist ein objektorientiertes Analogon von SDL. Die Bibliothek bietet einfachen Zugriff auf E/A-, Audio-, Datennetzwerk- und OpenGL-Funktionen.

Aber was, wenn wir dieser Bibliothek zusätzlich Qt-Funktionalität hinzufügen, die mächtigen Fähigkeiten von Signalen und Slots, Qt-Ressourcendateien unter Kontrolle bekommen? Was wird dafür benötigt?

  1. Laden Sie die neuesten SFML -Quellen von GitHub herunter.
  2. Quellen in eine Bibliothek kompilieren
  3. Erstellen Sie ein Qt-Projekt
  4. Verbinden Sie die Bibliothek mit dem Projekt
  5. Schreiben Sie ein Widget, das direkt über die SFML-Funktionalität gerendert wird

SFML erstellen

Im Allgemeinen können Sie vorkompilierte SFML-Bibliotheken herunterladen, aber wenn Sie einen anderen Compiler als den haben, mit dem diese Bibliotheken erstellt wurden, wird nichts entsprechend funktionieren. Daher experimentieren wir nicht, sondern kompilieren die Bibliothek gleich mit dem Compiler, mit dem wir das Projekt bauen werden.

SFML wird durch ein Projekt repräsentiert, das das Build-System CMAKE verwendet, das sich problemlos in Qt Creator öffnet.

Wenn das Projekt geöffnet ist, starten wir CMAKE und erstellen dann die Bibliothek selbst, sowohl im Debug-Modus als auch im Release-Modus, um zwei Sätze von Bibliotheken zu haben.

Das Build-Verzeichnis enthält das lib-Verzeichnis, das die für uns interessanten Bibliotheken enthält. Wir werden sie dem Qt-Projekt hinzufügen, das SFML verwenden wird.

Die folgenden Bibliotheken werden erstellt:

  • smfl-Audio
  • sfml-Grafiken
  • sfml-Netzwerk
  • sfml-Fenster
  • sfml-System

Qt-Projekt

Als nächstes erstellen Sie ein neues Qt-Projekt. Erstellen wir ein Projekt mit QWidget als Hauptanwendungsfenster. Nachdem das Projekt erstellt wurde, fügen Sie das SFML-Verzeichnis zum Projektstammverzeichnis hinzu, das zwei Verzeichnisse enthält: lib und include.

Alle kompilierten SFML-Bibliotheken müssen im lib-Verzeichnis abgelegt werden, sowohl für die Debug-Version als auch für die Release-Version. Und im include -Verzeichnis müssen Sie Dateien aus demselben include-Verzeichnis platzieren, das sich im Quellprojekt der SFML-Bibliothek befindet.

Als Ergebnis sieht die Struktur dieses Verzeichnisses in Ihrem Qt-Projekt wie folgt aus.

Nachdem die Bibliothek vorbereitet ist, muss sie über ein Profil in das Qt-Projekt eingebunden werden.

Schreiben Sie dazu folgende Zeilen in die Profildatei:

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 - bezeichnet den Speicherort der Pro-Projektdatei, in die wir die Projektkonfiguration und die Include-Dateien schreiben. Das heißt, wir suchen nach allen erforderlichen Bibliotheksdateien relativ zur Pro-Projektdatei.

Und jetzt fangen wir an, das Projekt selbst zu schreiben. Lassen Sie uns ein SFML-Widget erstellen, das in das Hauptanwendungs-Widget passt, um zu zeigen, dass Sie nicht das gesamte Anwendungsfenster aufgeben müssen, um SFML zu verwenden.

Die Projektstruktur wird wie folgt aussehen:

Wie Sie sehen, gibt es ein Hauptfenster-Widget Widget und ein qsqmlcanvas-Widget, in dem die SFML-Bibliothek funktioniert. Ich habe auch ein Bild über eine Qt-Ressourcendatei hinzugefügt ... Ja! Das ist möglich! Es ist möglich, Qt-Ressourcendateien in einer SFML-Bibliothek zu verwenden, es sind nur ein paar zusätzliche Codezeilen erforderlich, um die Bilder ordnungsgemäß aus der Ressourcendatei zu extrahieren.

Beginnen wir nun mit der Erstellung eines Projekts mit Qt und SFML. Es wurde beschlossen, eine Anwendung zu erstellen, in deren SFML-Widget sich das Qt-Logo drehen wird.

Projektprofil und main.cpp

Es wurde oben erwähnt, dass die Bibliothek in das Projekt aufgenommen wird, und die Datei main.cpp unterliegt keinen Änderungen, daher wird sie sich nicht darauf konzentrieren.

Widget.h

In diesem Projekt werde ich keinen Grafikdesigner verwenden, also schreiben wir manuell ein QGridLayout in die Header-Datei des Fensters, das das SFML-Widget hosten wird.

#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

Und im Konstruktor erstellen wir ein QSFMLCanvas-Widget und legen seine Platzierung im Anwendungsfenster fest.

#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

Analysieren wir den Inhalt der SFML-Header-Klasse des Widgets.

#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

Die Methoden onInit() und onUpdate() sind optional und Sie können sie tatsächlich benennen, wie Sie möchten. Wichtig ist, dass Sie zunächst alle erforderlichen Objekte hinzufügen und regelmäßig aktualisieren müssen.

Aber die Methode paintEngine() muss überschrieben werden, da wir die Qt-Rendering-Engine nicht verwenden werden, dann muss diese Methode nullptr. zurückgeben.

Die Methoden showEvent() und paintEvent() werden verwendet, um die SFML-Funktionalität und das anfängliche Rendern und Aktualisieren des Renderns von Objekten zu initialisieren. Dies sind Methoden, die Methoden der QWidget -Klasse sind, von der die SFML-Widget-Klasse abgeleitet ist. Das Neuzeichnen des SFML-Widgets mit diesen Methoden erfolgt innerhalb des Qt-Ereignis-Frameworks. Was bedeutet das? Tatsache ist, dass die paintEvent() -Methode aufgerufen werden kann, indem die repaint() -Methode aufgerufen wird, repaint() löst ein repaint-Ereignis innerhalb des Aufrufstapels der Qt-Anwendung aus, also ist es nicht überraschend das zum Neuzeichnen wird nicht durch einen direkten Aufruf der Methode paintEvent(). ausgeführt

Sie haben vielleicht auch bemerkt, dass diese Klasse Mehrfachvererbung von zwei Klassen verwendet: QWidget und sf::RenderWindow.

Wir benötigen die Vererbung von QWidget , um ein SFML-Widget überall in die Anwendungsschnittstelle einbetten zu können und auf die Funktionalität von Qt-Signalen und -Slots zuzugreifen, während die Vererbung von sf::RenderWindow für den Zugriff erforderlich ist Funktionalität dieser Bibliothek, außerdem arbeitet sie direkt mit OpenGL zusammen.

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();
}

Projekt herunterladen

ACHTUNG: Das Projekt wurde unter KDE NEON 5.8.2 erstellt und ist ein Beispiel, dessen korrekter Start unter anderen Betriebssystemen nicht gewährleistet ist, insbesondere müssen Sie die SFML-Bibliothek für Ihre Zielplattform und erstellen mit deinem Compiler.

Рекомендуємо хостинг TIMEWEB
Рекомендуємо хостинг TIMEWEB
Stabiles Hosting des sozialen Netzwerks EVILEG. Wir empfehlen VDS-Hosting für Django-Projekte.

Magst du es? In sozialen Netzwerken teilen!

F
  • 10. Oktober 2017 11:41

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

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

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

F
  • 10. Oktober 2017 12:18

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

Evgenii Legotckoi
  • 10. Oktober 2017 12:21

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

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

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

Evgenii Legotckoi
  • 10. Oktober 2017 12:26

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

F
  • 10. Oktober 2017 12:29

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

Evgenii Legotckoi
  • 10. Oktober 2017 12:32

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

RenderWindow::create(static_cast<sf::WindowHandle>(winId()));
F
  • 10. Oktober 2017 12:35

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


F
  • 10. Oktober 2017 12:36

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

Evgenii Legotckoi
  • 10. Oktober 2017 12:38

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

F
  • 10. Oktober 2017 12:38

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

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

mingw


F
  • 10. Oktober 2017 12:43

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

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

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

Kommentare

Nur autorisierte Benutzer können Kommentare posten.
Bitte Anmelden oder Registrieren
Letzte Kommentare
ИМ
Игорь Максимов5. Oktober 2024 07:51
Django – Lektion 064. So schreiben Sie eine Python-Markdown-Erweiterung Приветствую Евгений! У меня вопрос. Можно ли вставлять свои классы в разметку редактора markdown? Допустим имея стандартную разметку: <ul> <li></li> <li></l…
d
dblas55. Juli 2024 11:02
QML - Lektion 016. SQLite-Datenbank und das Arbeiten damit in QML Qt Здравствуйте, возникает такая проблема (я новичок): ApplicationWindow неизвестный элемент. (М300) для TextField и Button аналогично. Могу предположить, что из-за более новой верси…
k
kmssr8. Februar 2024 18:43
Qt Linux - Lektion 001. Autorun Qt-Anwendung unter Linux как сделать автозапуск для флэтпака, который не даёт создавать файлы в ~/.config - вот это вопрос ))
Qt WinAPI - Lektion 007. Arbeiten mit ICMP-Ping in Qt Без строки #include <QRegularExpressionValidator> в заголовочном файле не работает валидатор.
EVA
EVA25. Dezember 2023 10:30
Boost - statisches Verknüpfen im CMake-Projekt unter Windows Ошибка LNK1104 часто возникает, когда компоновщик не может найти или открыть файл библиотеки. В вашем случае, это файл libboost_locale-vc142-mt-gd-x64-1_74.lib из библиотеки Boost для C+…
Jetzt im Forum diskutieren
J
JacobFib17. Oktober 2024 03:27
добавить qlineseries в функции Пользователь может получить любые разъяснения по интересующим вопросам, касающимся обработки его персональных данных, обратившись к Оператору с помощью электронной почты https://topdecorpro.ru…
JW
Jhon Wick1. Oktober 2024 15:52
Indian Food Restaurant In Columbus OH| Layla’s Kitchen Indian Restaurant If you're looking for a truly authentic https://www.laylaskitchenrestaurantohio.com/ , Layla’s Kitchen Indian Restaurant is your go-to destination. Located at 6152 Cleveland Ave, Colu…
КГ
Кирилл Гусарев27. September 2024 09:09
Не запускается программа на Qt: точка входа в процедуру не найдена в библиотеке DLL Написал программу на C++ Qt в Qt Creator, сбилдил Release с помощью MinGW 64-bit, бинарнику напихал dll-ки с помощью windeployqt.exe. При попытке запуска моей сбилженной программы выдаёт три оши…
F
Fynjy22. Juli 2024 04:15
при создании qml проекта Kits есть но недоступны для выбора Поставил Qt Creator 11.0.2. Qt 6.4.3 При создании проекта Qml не могу выбрать Kits, они все недоступны, хотя настроены и при создании обычного Qt Widget приложения их можно выбрать. В чем может …

Folgen Sie uns in sozialen Netzwerken