如何将第三方应用程序(使用 SDL2)嵌入到 QWidget 中?

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

我正在尝试在我的 PyQt5 应用程序中显示另一个进程 window。因为我在 Linux 上工作,所以我得到了这个 python gist 与 x11 一起工作。我将代码更改为从 PID 抓取,然后忙等到 window 打开并使用 subprocess.Popen 启动程序 然而,这种方法存在一些问题:

它有点不稳定。有时它无法获取 window,我认为这与在启动时直接从 QSettings 恢复布局有关。 我收到这些错误消息:

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

这是什么意思?

但是,如果它不依赖于平台,我会接受不稳定的解决方案!我希望有一个独立于平台的解决方案,至少对于 Linux 和 Windows。我猜在我的 PyQt5 应用程序中使用 xlib 会将它绑定到 Linux。那么下一个问题是有一种独立于平台的方式来显示在我的 PyQt5 应用程序中使用 SDL2 的第三方应用程序吗?我不确定,但也许在我的应用程序中嵌入 xserver 是一个解决方案,但我对 xserver 了解不多,我认为这不是一件容易的事。

理想的解决方案纯粹是用 Python 编写的(如果没有其他方法,也可以用 C++ 编写),但是由于第三方应用程序是开源的 (C++),我可以这样重写应用程序一种支持被 PyQt5 独立抓取的平台的方法。这会很不方便,因为我无法使用预构建的二进制文件,但如果这是唯一的方法,并且付出了合理的努力,我会接受它。我的即时想法是通过 SDL 获取 window ID 并将其以某种方式发送到我的应用程序。我的研究没有就如何做到这一点以及 my attempt 使用

产生任何结果
SDL_GetWindowID(SDL_Window * window);

失败,因为很快我(认为我)发现它返回了 SDL 实习生 window ID 而不是操作系统 WID。所以我的问题是:如何从 SDL 获取 Window ID?或者如何让我的 PyQt5 应用程序获取它的 window?

也许最好(也是最困难)的解决方案是重写开源应用程序,以在 QWindow 中显示其内容并发送此 ID,但我不知道如何接近那个,也不是 link 这个应用程序与 qt。也许在这一点上值得注意,我是 Qt 的新手,这就是我使用 PyQt5 的原因。

你必须使用SDL_SysWMinfo来获取Window的id,它根据OS提供不同的属性,例如X11你必须使用x11.window 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()