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)
0 Comments:
Post a Comment
Note: Only a member of this blog may post a comment.