【问题标题】:How to embed a third party application (using SDL2) into a QWidget?如何将第三方应用程序(使用 SDL2)嵌入到 QWidget 中?
【发布时间】:2021-05-22 12:02:12
【问题描述】:

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

它有点不稳定。有时它无法抓取窗口,我认为这与在启动时直接从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 获取窗口 ID 并以某种方式将其发送到我的应用程序。我的研究没有产生任何关于如何做到这一点和my attempt 使用的结果

SDL_GetWindowID(SDL_Window * window);

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

也许最好的(也是最困难的)解决方案是重写开源应用程序,以在 QWindow 中显示其内容并将此 ID 发送过来,但我不知道如何处理,也不知道如何链接这个应用程序与 qt。也许在这一点上值得注意的是,我对 Qt 还很陌生,这就是我使用 PyQt5 的原因。

【问题讨论】:

  • 您指出:我的研究没有产生任何关于如何做到这一点以及我尝试使用的结果...,您能否提供minimal reproducible example 的内容你指向
  • 我尝试过的最小示例已经给出。 SDL_GetWindowID(SDL_Window * window); 在窗口句柄上返回 2 并尝试使用 xprop - id 2 获取窗口信息导致错误。这个oneliner是否需要一个可重现的例子?
  • 不,我想要一个可以执行和重现的代码
  • 那么您认为 SDL_GetWindowID 应该返回当前操作系统的实际 WindowID 吗? e. G。 X11 WID?
  • 是的,在 C++ 中它可以工作

标签: python pyqt5 cross-platform embed sdl-2


【解决方案1】:

你必须使用SDL_SysWMinfo来获取Window的id,它根据操作系统提供不同的属性,例如对于X11你必须使用x11.window,对于windows你必须使用win.window

ma​​in.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()

【讨论】:

  • 终于可以拿到Window ID了,非常感谢!最后一个问题,你知道一种不需要重写第三方应用程序的方法吗?
  • @Sewbacca 正是 xlib 和其他允许使用操作系统库获取 id 的库。如果您想使用不同的操作系统,则使用每个操作系统的本机库并创建一个包装器,也就是说:if is_x11: x11_native_lib.windows_id elif is_windows: windows_native_lib.windows_id。它没有库,因此您可以创建它。
  • 现在看来很明显了:如果可以从python中抓取xlib来找到窗口,那么windows也一定有解决方案。我一定无法看到所有树木的树林。非常感谢,感谢您的帮助:)
猜你喜欢
  • 2020-02-02
  • 2016-02-15
  • 1970-01-01
  • 1970-01-01
  • 2012-06-16
  • 1970-01-01
  • 1970-01-01
  • 2012-04-05
  • 1970-01-01
相关资源
最近更新 更多