PHPFixing
  • Privacy Policy
  • TOS
  • Ask Question
  • Contact Us
  • Home
  • PHP
  • Programming
  • SQL Injection
  • Web3.0

Monday, September 12, 2022

[FIXED] How to embed a third party application (using SDL2) into a QWidget?

 September 12, 2022     cross-platform, embed, pyqt5, python, sdl-2     No comments   

Issue

I am trying to display another processes window inside my PyQt5 application. Since I work on Linux, I've got this python gist working with x11. I changed the code to grab from PID instead and busy wait until the window is open and launch the program with subprocess.Popen However there are a few problems with this approach:

It is somehow unstable. Sometimes it fails to grab the window, and I think it has something to do with restoring layout from QSettings directly on startup. I get these error messages:

qt.qpa.xcb: QXcbConnection: XCB error: 3 (BadWindow), sequence: 2577, resource id: 127926283, major code: 18 (ChangeProperty), minor code: 0
qt.qpa.xcb: QXcbConnection: XCB error: 3 (BadWindow), sequence: 2578, resource id: 127926283, major code: 12 (ConfigureWindow), minor code: 0

What does it mean?

However I would accept an unstable solution, if it wasn't platform dependent! I would love to have a platform independent solution, at least for Linux and Windows. Using the xlib inside my PyQt5 application will tie it onto Linux I guess. So the next question is there a platform independent way to display a third party application which is using SDL2 inside my PyQt5 app? I'm not sure, but maybe it would be a solution to embed a xserver inside my application, but I don't know much about xserver and I don't think it would be an easy task.

The dream solution would be purly written in Python (or in C++ if there is no other way), but since the third party application is open source (C++), I could rewrite the application in such a way to support being plattform independently grabbed by PyQt5. It would be inconvinient, because I couldn't work with the prebuild binaries, but if that is the only way, with a reasonable amount of effort, I would take it. My instant idea was to get the window ID via SDL and send it somehow over to my application. My researches didn't yield any results on how to do that and my attempt to use

SDL_GetWindowID(SDL_Window * window);

failed, because soon I (think I) found out, that it returned the SDL intern window ID rather than the OSes WID. So my question here is: How do I get the Window ID from SDL? Or how to enable my PyQt5 app to grab its window?

Maybe the best (and most difficult) solution would be to rewrite the open source app in such a way to display their content inside a QWindow and sent this ID over, but I have no idea how to approach that, nor to link this app with qt. Maybe it is worth noting at this point, that I'm pretty new to Qt, that's why I am using PyQt5.


Solution

You must use SDL_SysWMinfo to obtain the id of the Window, it offers different attributes depending on the OS, for example for X11 you must use x11.window and for windows win.window:

main.cpp

#include <SDL2/SDL.h>
#include <SDL2/SDL_syswm.h>
#include <unistd.h>

int main(int argc, char const *argv[])
{
    uint32_t nix = 0;
    auto win = SDL_CreateWindow("Test", 0, 0, 400, 200, nix);

    SDL_RaiseWindow(win);

    SDL_SysWMinfo wmInfo;
    SDL_VERSION(&wmInfo.version);
    SDL_GetWindowWMInfo(win, &wmInfo);

    printf("ID: %d\n", wmInfo.info.x11.window);
    fflush(stdout);

    while (1) { usleep(100000); }
    return 0;
}
import os
import sys
from pathlib import Path

from PyQt5 import QtCore, QtGui, QtWidgets

CURRENT_DIRECTORY = Path(__file__).resolve().parent


def find_id(executable):
    win_id = -1
    process = QtCore.QProcess()
    process.setProgram(executable)
    loop = QtCore.QEventLoop()

    def handle_readyReadStandardOutput():
        text = process.readAllStandardOutput().data().decode()
        _, id_str = text.split()
        nonlocal win_id
        win_id = int(id_str)
        loop.quit()

    process.readyReadStandardOutput.connect(handle_readyReadStandardOutput)
    process.start()
    loop.exec_()
    return win_id


def main():
    app = QtWidgets.QApplication(sys.argv)

    win_id = find_id(os.fspath(CURRENT_DIRECTORY / "test"))

    main_widget = QtWidgets.QWidget()
    layout = QtWidgets.QVBoxLayout(main_widget)

    window = QtGui.QWindow.fromWinId(win_id)
    widget = QtWidgets.QWidget.createWindowContainer(window)

    button = QtWidgets.QPushButton("Close")
    button.clicked.connect(main_widget.close)

    layout.addWidget(widget)
    layout.addWidget(button)

    main_widget.show()
    app.exec_()


if __name__ == "__main__":
    main()


Answered By - eyllanesc
Answer Checked By - Cary Denson (PHPFixing Admin)
  • Share This:  
  •  Facebook
  •  Twitter
  •  Stumble
  •  Digg
Newer Post Older Post Home

0 Comments:

Post a Comment

Note: Only a member of this blog may post a comment.

Total Pageviews

Featured Post

Why Learn PHP Programming

Why Learn PHP Programming A widely-used open source scripting language PHP is one of the most popular programming languages in the world. It...

Subscribe To

Posts
Atom
Posts
Comments
Atom
Comments

Copyright © PHPFixing