Skip to content

Qt for WebAssembly

Specific issues when using Qt for WebAssembly

402 Topics 1.6k Posts
  • Links for Qt for WebAssembly

    Pinned Unsolved
    6
    7 Votes
    6 Posts
    3k Views
    S

    @tansgumus said in Links for Qt for WebAssembly:

    Fireofox 76.0 (64-bit)

    Fireofox 76.0 (64-bit) nooooooooooooooooooo this very bad

  • run Qt WASM across private network

    Unsolved
    1
    0 Votes
    1 Posts
    3 Views
    No one has replied
  • WebAssembly create and download pdf and xlsx files

    Unsolved
    2
    0 Votes
    2 Posts
    44 Views
    W

    I was able to download the file in the following way, the backend that I have made in nestjs
    generates the pdf file, I consume it from the front with qnetworkaccesmanager
    and finally QFileDialog::saveFileContent(reply->readAll(), "peport.pdf" )Captura de pantalla 2024-04-30 a la(s) 9.01.17 p. m..png

  • 1 Votes
    3 Posts
    49 Views
    S

    With normal caveats regarding "threads" (web workers in web parlance) you could just run your data simulation in a thread and then use the main thread to update your VBO and render.

  • Qt Creator runs WebAssembly in Internet Explorer

    Solved
    3
    0 Votes
    3 Posts
    107 Views
    8Observer88

    It works now on Chrome. I just have restarted Qt Creator.

  • How to include 3rd party library

    Unsolved
    6
    0 Votes
    6 Posts
    133 Views
    8Observer88

    @Saju I didn't use OpenCV. But try these steps that I use for another libraries:

    Add C:\emsdk\upstream\emscripten to the Path variable cd opencv-1.2.3 && mkdir build && cd build emcmake cmake .. emmake make INCLUDEPATH += "E:/libs/opencv-1.2.3/include" LIBS += -L"E:/libs/opencv-1.2.3/build/lib" LIBS += -lopencv
  • Problem using QFileDialog::saveFileContent on Chrome

    Unsolved
    3
    0 Votes
    3 Posts
    83 Views
    R

    Hello, @ryan1969.

    The problem was the saveFileContent function was being called at the end of a procedure that took a long time. I guess the browser could not link the function with the click event as you said. So I had to add another button for the user to click when the procedure finishes. This made everything work OK.

    Thank you for the help.

  • Qt6.7 Drag and Drop on WebAssembly

    Unsolved
    7
    0 Votes
    7 Posts
    141 Views
    JonBJ

    @PEPSoares said in Qt6.7 Drag and Drop on WebAssembly:

    QFile cannot open the file because the file does not exist....

    That does not surprise me. One would assume that the file is clearly a temporary file, valid somehow during the drag & drop, then removed. That is why I asked only if you could access the directory, not the file.

    Btw: QDir can access "/qt/tmp"

    The question was for //qt/tmp, not /qt/tmp.

  • How to use fileDialog with QML for Web Assembly

    Unsolved
    5
    0 Votes
    5 Posts
    218 Views
    J

    @Gilboonet it's been a while since you've asked the question, but as I found it then I'll just reply for future generations :)

    Basically you can't directly access any files on your devices from WASM. You need to use Browser API to achieve this, so the only option is to call native browser file picker for open & browser download for saving. You can pick which file to open, but when you save, then it's gonna be downloaded by a browser like any other file from a web (yes, it'll be saved in Downloads folder).

    It seems that Qt also supports file picking on WASM but unfortunately I didn't have a chance to test it yet: https://doc.qt.io/qt-6/qfiledialog.html#getOpenFileContent

    Here's a great example on how to use native file picker in Qt project:

    https://github.com/msorvig/qt-webassembly-examples/tree/master/gui_localfiles

    Here's my code, heavily based on above example, if you're interested:

    std::function<void(const QByteArray &, const QString &)> fileDataReadyCallback; extern "C" EMSCRIPTEN_KEEPALIVE void wasmFileDataReadyCallback(char *content, size_t contentSize, const char *fileName) { if (fileDataReadyCallback == nullptr) return; QByteArray data(content, contentSize); free(content); fileDataReadyCallback(data, QString::fromUtf8(fileName)); fileDataReadyCallback = nullptr; } void WasmPlatformIntegration::openFile() { openFile("*", [](const QByteArray &content, const QString &fileName) { qDebug() << "load file" << fileName << "size" << content.size(); }); } void WasmPlatformIntegration::openFile(const QString &fileNameFilter, std::function<void(const QByteArray &, const QString &)> fileDataReady) { if (::fileDataReadyCallback) puts("Warning: Concurrent loadFile() calls are not supported. Cancelling " "earlier call"); ::fileDataReadyCallback = nullptr; ::wasmFileDataReadyCallback(nullptr, 0, nullptr); ::fileDataReadyCallback = fileDataReady; EM_ASM_( { const fileNameFilter = UTF8ToString($0); // Crate file file input which whil display the native file dialog let fileElement = document.createElement("input"); document.body.appendChild(fileElement); fileElement.type = "file"; fileElement.style = "display:none"; fileElement.accept = fileNameFilter; fileElement.onchange = function(event) { const files = event.target.files; // Read files for (var i = 0; i < files.length; i++) { const file = files[i]; var reader = new FileReader(); reader.onload = function() { const name = file.name; let contentArray = new Uint8Array(reader.result); const contentSize = reader.result.byteLength; const heapPointer = _malloc(contentSize); const heapBytes = new Uint8Array(Module.HEAPU8.buffer, heapPointer, contentSize); heapBytes.set(contentArray); // Null out the first data copy to enable GC reader = null; contentArray = null; // Call the C++ file data ready callback ccall("wasmFileDataReadyCallback", null, [ "number", "number", "string" ], [ heapPointer, contentSize, name ]); }; reader.readAsArrayBuffer(file); } // Clean up document document.body.removeChild(fileElement); }; // onchange callback // Trigger file dialog open fileElement.click(); }, fileNameFilter.toUtf8().constData()); } void WasmPlatformIntegration::saveFile(const QByteArray &content, const QString &fileNameHint) { EM_ASM_( { const contentPointer = $0; const contentLength = $1; const fileNameHint = UTF8ToString($2); const fileContent = Module.HEAPU8.subarray(contentPointer, contentPointer + contentLength); const arrayBuffer = fileContent.slice(); const uint8Array = new Uint8Array(arrayBuffer); uint8Array.fill(255); const fileblob = new Blob([arrayBuffer]); // Create a hidden download link and click it programatically let link = document.createElement("a"); document.body.appendChild(link); link.download = fileNameHint; link.href = window.URL.createObjectURL(fileblob); link.style = "display:none"; link.click(); document.body.removeChild(link); }, content.constData(), content.size(), fileNameHint.toUtf8().constData()); }
  • QtLoader is deprecated in qt6.6.0

    Unsolved
    7
    0 Votes
    7 Posts
    575 Views
    E

    If you want to fetch your wasm file from a server using the new Qt 6.6 qt loader api you can do it like this:

    const wasmPromise = WebAssembly.compileStreaming(fetch(url)); const instance = await qtLoad({ qt: { onLoaded: () => { loadingScreen.style.display = "none"; }, onExit: exitData => { console.log(exitData); }, entryFunction: window.createQtAppInstance, containerElements: [mainScreen], module: wasmPromise, } });
  • RuntimeError: index out of bounds

    Unsolved
    2
    0 Votes
    2 Posts
    117 Views
    E

    First make sure you are compiling with the correct Emscripten version for the Qt version you are using. For Qt 6.5.2 it should be Emscripten 3.1.25. If that doesn't work add this line to your cmake file: target_link_options(PocketSolver PUBLIC -s STACK_SIZE=5MB), this should fix your error.

  • How to extend the Cube OpenGL ES 2.0 example for 3D meshes?

    Unsolved
    6
    0 Votes
    6 Posts
    135 Views
    8Observer88

    I created this bug report about the problem above with anti-aliasing on QOpenGLWidget: The setSamples() method doesn't work on QOpenGLWidget for WebAssembly

    @Even-Oscar-Andersen answered in the bug report comments:

    reproduced on 6.6.2,

    This might be fixed in the not yet released 6.8.x (you will have to build yourself from dev)

    I think that rebuilding Qt dev from source may be useful for those who want to use QOpenGLWidget with Qt GUI on WASM with anti-aliasing. Maybe some people don’t need anti-aliasing. I don't need Qt GUI for my current tasks but I need anti-aliasing. I prefer to use QOpenGLWindow because it allows to make smooth animation for WASM without QTimer (animation with timer isn't smooth) using the setSwapInterval(1) method and the frameSwapped signal:

    OpenGLWindow.cpp

    OpenGLWindow::OpenGLWindow() { setTitle("OpenGL ES 2.0, Qt6, C++"); resize(m_initialWindowWidth, m_initialWindowHeight); QSurfaceFormat surfaceFormat; surfaceFormat.setDepthBufferSize(24); surfaceFormat.setSamples(4); surfaceFormat.setSwapInterval(1); connect(this, SIGNAL(frameSwapped()), this, SLOT(update())); setFormat(surfaceFormat); }

    OpenGLWindow.h

    #include <QtCore/QElapsedTimer> class OpenGLWindow : public QOpenGLWindow, private QOpenGLFunctions { private: QElapsedTimer m_elapsedTimer; } void OpenGLWindow::paintGL() { float dt = m_elapsedTimer.elapsed() / 1000.f; m_elapsedTimer.restart(); qDebug() << dt; }
  • How to set up Assimp for Qt6 MinGW 64-bit

    Solved
    11
    0 Votes
    11 Posts
    182 Views
    8Observer88

    Botje wrote the next comment on Stack Overflow:

    libassimp.a does not contain zlib. If you inspect the makefile generated by cmake you will see it just collects all the object files and does not add libz in any way. That is reserved for the final step where your application links to libassimp.a, because other dependencies might also need zlib and if each brings its own copy you get conflicts.

  • 0 Votes
    2 Posts
    93 Views
    E

    For windows, visual studio build with Qt 6.6.2 installed in C:\Qt

    Note the two different ways of running cmake

    vcvars64 (for msvc build) git clone https://github.com/msorvig/qt-webassembly-examples.git cd qt-webassembly-examples\gui_webgltest C:\Qt\6.6.2\wasm_singlethread\bin\qt-cmake .
    (creates openglwindow.html) cmake --build .
    (creates .js, .wasm) start emrun openglwindow.html
  • 0 Votes
    4 Posts
    89 Views
    jsulmJ

    @shome Did you also do a complete rebuild after changing the pro file?

  • Empty headers in WASM 6.6.2.

    Unsolved
    3
    0 Votes
    3 Posts
    120 Views
    D

    @doumdi said in Empty headers in WASM 6.6.2.:

    I have the exact same problem, C++ code for requests generates unusable headers (empty or non ISO-8859-1). Any way to fix this problem ?

    I think this might be fixed in the new release : https://bugreports.qt.io/browse/QTBUG-122893

  • qmake for wasm unable to find opengl libraries

    Unsolved
    11
    0 Votes
    11 Posts
    240 Views
    S

    @8Observer8, @SGaist, @jsulm :

    qt 6.6.2, emscripten: 3.1.37, ubuntu: 22.04

    i am trying to compile the code for loading obj using assimp (for wasm). I get error qglobal.h: fatal error: 'type_traits' file not found.

    My question is how to avoid this error?

    /home/neo/Desktop/softwares/qt662_wasm/qt6/qtbase/src/corelib/global/qglobal.h:13:12: fatal error: 'type_traits' file not found # include <type_traits>

    However, I have /usr/include/c++/11/type_traits file. The file at /home/neo/Desktop/softwares/qt662_wasm/qt6/qtbase/src/corelib/global/qglobal.h reads:

    #ifdef __cplusplus # include <type_traits> ... #endif

    I think this problem has been faced before as documented in type_traits-file-not-found-while-building-qwt6-1-4. This is occuring if we compile for wasm too.

    My main.cpp and test.pro are as below. I use assimp to load 3D meshes, which I have built using emscripten using the following steps:

    git clone https://github.com/assimp/assimp.git cd assimp && mkdir build && cd build emcmake cmake .. emmake make

    main.cpp

    #include <QtWidgets/QApplication> //#include <QtGui/QOpenGLWidget> #include <QOpenGLWidget> #include <QtWidgets/QMessageBox> //#include <QtGui/QOpenGLBuffer> #include <QOpenGLBuffer> //#include <QtGui/QOpenGLShaderProgram> #include <QOpenGLShaderProgram> #include <QtGui/QMatrix4x4> #include <QtGui/QVector3D> #include <QtCore/QDebug> #include <assimp/Importer.hpp> #include <assimp/scene.h> #include <assimp/postprocess.h> #include <QMouseEvent> //#include <cstdarg> class OpenGLWidget : public QOpenGLWidget { public: OpenGLWidget(QWidget *parent = nullptr) : QOpenGLWidget (parent) { setWindowTitle("Qt load obj using shaders"); resize(600, 600); } private: QOpenGLBuffer m_vertPosBuffer; QOpenGLShaderProgram m_program; int m_numVertices; void initializeGL() override { glClearColor(0.1f, 0.1f, 0.1f, 1.f); glEnable(GL_DEPTH_TEST); Assimp::Importer importer; const char *path = "./REC-3MxwG6i6jfG8ig2.obj"; const aiScene *scene = importer.ReadFile(path, aiProcess_Triangulate | aiProcess_FlipUVs); if (!scene || scene->mFlags & AI_SCENE_FLAGS_INCOMPLETE || !scene->mRootNode) { qDebug() << "Assimp Error:" << importer.GetErrorString(); QMessageBox::critical(this, "Assimp Error:", importer.GetErrorString()); return; } m_numVertices = scene->mMeshes[0]->mNumVertices; float vertPositions[m_numVertices * 3]; int vertPosIndex = 0; for (int i = 0; i < m_numVertices; i++) { vertPositions[vertPosIndex++] = scene->mMeshes[0]->mVertices[i].x; vertPositions[vertPosIndex++] = scene->mMeshes[0]->mVertices[i].y; vertPositions[vertPosIndex++] = scene->mMeshes[0]->mVertices[i].z; // qDebug() << scene->mMeshes[0]->mVertices[i].x << ", " // << scene->mMeshes[0]->mVertices[i].y << ", " // << scene->mMeshes[0]->mVertices[i].z; } m_vertPosBuffer.create(); m_vertPosBuffer.bind(); m_vertPosBuffer.allocate(vertPositions, sizeof(vertPositions)); const char *vertShaderSrc = "#version 330 core\n" "in vec3 aPosition;" "uniform mat4 uModelMatrix;" "void main()" "{" " gl_Position = uModelMatrix * vec4(aPosition, 1.0);" "}"; const char *fragShaderSrc = "#version 330 core\n" "out vec4 fragColor;" "void main()" "{" " fragColor = vec4(0.8, 0.2, 0.2, 1.0);" "}"; m_program.create(); m_program.addShaderFromSourceCode(QOpenGLShader::Vertex, vertShaderSrc); m_program.addShaderFromSourceCode(QOpenGLShader::Fragment, fragShaderSrc); m_program.link(); QMatrix4x4 modelMatrix; modelMatrix.scale(0.5); m_program.bind(); m_program.setUniformValue("uModelMatrix", modelMatrix); m_program.setAttributeBuffer("aPosition", GL_FLOAT, 0, 3); m_program.enableAttributeArray("aPosition"); } void resizeGL(int w, int h) override { glViewport(0, 0, w, h); } void paintGL() override { glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); glDrawArrays(GL_TRIANGLES, 0, m_numVertices); } }; int main(int argc, char *argv[]) { QApplication a(argc, argv); OpenGLWidget w; w.show(); return a.exec(); }

    test.pro

    QT += core gui opengl openglwidgets # Add the path to the Assimp library directory if needed LIBS += -L/home/neo/Desktop/softwares/assimp/build/lib # Link against the Assimp library LIBS += -lassimp # Add the path to the Assimp header files if needed INCLUDEPATH += /home/neo/Desktop/softwares/assimp/include INCLUDEPATH += /usr/include/x86_64-linux-gnu INCLUDEPATH += /usr/include/linux # Add include path to Emscripten's C++ standard library headers INCLUDEPATH += /home/neo/Desktop/softwares/emsdk/upstream/emscripten/system/include # the system's C++ standard library QMAKE_CXXFLAGS += -stdlib=libstdc++ # Additional compiler options for Assimp QMAKE_CXXFLAGS += -Wno-deprecated-declarations TEMPLATE = app TARGET = AssimpExample CONFIG += c++11 SOURCES += main.cpp
  • Dealing with keyboard layout for input on Qt WebAssembly

    Unsolved
    2
    2 Votes
    2 Posts
    99 Views
    8Observer88

    All resources (sprites, music and sounds) have been replaced with free ones. You can see a list of free resources here. For example, I took the sprites here: https://webfussel.itch.io/more-bit-8-bit-mario

    itch.io page Click this link to play in the browser (use WAD or arrow keys to move and jump) Download EXE for Windows 10, 64-bit APK-file for Android

    mario-2d-jumps-webfussel-opengles2-qt6-cpp-android.gif

  • Is openGL, GLU, GLUT not supported in qt for wasm

    Unsolved
    7
    0 Votes
    7 Posts
    196 Views
    8Observer88

    @shome said in Is openGL, GLU, GLUT not supported in qt for wasm:

    My actual code uses the opengl functions gluPerspective, glShadeModel, glLightModelfv, glLightfv, glColorMaterial, glMaterialfv, glLightModeli, glTranslatef, and glMultMatrixd.

    WebGL doesn't support these functions. You should use OpenGL ES, shaders and QMatrix4x4 instead.

    My example can help you. It draws a triangle. You can run it in the browser: https://65fb01d024b197009c29122c--lustrous-croissant-656612.netlify.app/ It is a free hosting: netlify.com My example uses QOpenGLWindow instead of QOpenGLWidget. QOpenGLWindow is better for WebAssembly.

    pro:

    QT += core gui opengl widgets win32: LIBS += -lopengl32 CONFIG += c++17 SOURCES += \ main.cpp

    main.cpp

    #include <QtGui/QOpenGLFunctions> #include <QtGui/QSurfaceFormat> #include <QtOpenGL/QOpenGLBuffer> #include <QtOpenGL/QOpenGLWindow> #include <QtOpenGL/QOpenGLShaderProgram> #include <QtWidgets/QApplication> class OpenGLWindow : public QOpenGLWindow, private QOpenGLFunctions { public: OpenGLWindow() { resize(350, 350); setTitle("OpenGL ES 2.0 Triangle"); } private: void initializeGL() override { initializeOpenGLFunctions(); glClearColor(0.4f, 0.8f, 0.9f, 1.f); qDebug() << "OpenGL Version:" << (const char*) glGetString(GL_VERSION) << "\n"; qDebug() << "GLSL Version:" << (const char*) glGetString(GL_SHADING_LANGUAGE_VERSION) << "\n"; qDebug() << "OpenGL Vendor:" << (const char*) glGetString(GL_VENDOR) << "\n"; QString vertexShaderSource = "attribute vec2 aPosition;\n" "void main()\n" "{\n" " gl_Position = vec4(aPosition, 0.0, 1.0);\n" "}\n"; QString fragmentShaderSource = "#ifdef GL_ES\n" "precision mediump float;\n" "#endif\n" "void main()\n" "{\n" " gl_FragColor = vec4(1.0, 0.0, 0.0, 1.0);\n" "}\n"; m_program.create(); m_program.addShaderFromSourceCode(QOpenGLShader::ShaderTypeBit::Vertex, vertexShaderSource); m_program.addShaderFromSourceCode(QOpenGLShader::ShaderTypeBit::Fragment, fragmentShaderSource); m_program.link(); m_program.bind(); float vertPositions[] = { -0.5f, -0.5f, 0.5f, -0.5f, 0.f, 0.5f }; m_vertPosBuffer.create(); m_vertPosBuffer.bind(); m_vertPosBuffer.allocate(vertPositions, sizeof(vertPositions)); } void paintGL() override { m_program.bind(); m_vertPosBuffer.bind(); m_program.setAttributeBuffer("aPosition", GL_FLOAT, 0, 2); m_program.enableAttributeArray("aPosition"); glClearColor(0.4f, 0.8f, 0.9f, 1.f); glClear(GL_COLOR_BUFFER_BIT); glDrawArrays(GL_TRIANGLES, 0, 3); } private: QOpenGLShaderProgram m_program; QOpenGLBuffer m_vertPosBuffer; }; int main(int argc, char *argv[]) { QApplication::setAttribute(Qt::ApplicationAttribute::AA_UseDesktopOpenGL); QApplication app(argc, argv); OpenGLWindow w; w.show(); return app.exec(); }
  • Keyboard on chrome android always pop up.

    Unsolved
    1
    0 Votes
    1 Posts
    57 Views
    No one has replied