Linux x11环境下Qt应用实现多进程窗口嵌入
效果展示如图:
实现说明启动外部进程,通过进程id获取窗口winid,然后通过QWindow::fromWinId获取QWindow,使用QWidget::createWindowContainer创建包含进程界面QWidget。
embedexternalapp.cpp
embedexternalapp::embedexternalapp(QWidget *parent)
: QWidget(parent)
{
QStringList programs;
programs << "/usr/bin/gedit"
<< "/usr/bin/kompare"
<< "/usr/bin/gnome-help";
foreach (QString pro, programs) {
QProcess process;
qint64 pid;
if (process.startDetached(pro, QStringList(), "", &pid)) {
pidlist.push_back(pid);
}
}
QThread::currentThread()->msleep(1500);
//等待一会,虽然进程启动但如果窗口没有显示则无法通过Pid获取窗口Id
QVBoxLayout *mainlayout = new QVBoxLayout(this);
foreach (qint64 pid, pidlist) {
unsigned long winid = get_win_id_from_pid(pid);
if (0 == winid) {
QProcess proc;
proc.execute(QString("kill -9 %1").arg(pid));
continue;
}
QWindow *window = QWindow::fromWinId(winid);
window->setFlags(Qt::FramelessWindowHint);
QWidget *widget = QWidget::createWindowContainer(window);
widget->setMinimumSize(800, 600);
mainlayout->addWidget(widget);
}
QWidget *pembedwidget = new QWidget;
pembedwidget->setLayout(mainlayout);
QScrollArea *pscroll = new QScrollArea;
pscroll->setWidget(pembedwidget);
QVBoxLayout *layout = new QVBoxLayout;
layout->addWidget(pscroll);
this->setLayout(layout);
}
utils.cpp
#include "utils.h" #include#include #include
#include static std::list _result; unsigned long get_win_id_from_pid(int pid) { unsigned long winid = 0; _result.clear(); // Get the PID property atom. Display *display = XOpenDisplay(0); Window wRoot = XDefaultRootWindow(display); Atom _atomPID = XInternAtom(display, "_NET_WM_PID", True); if (_atomPID == None) { return 0; } search(wRoot, display, _atomPID, pid); if (_result.size() > 0) { winid = *_result.begin(); } qInfo() << _result.size(); return winid; } void search(unsigned long w, void *display, unsigned long _atomPID, int _pid) { Display *_display = static_cast (display); // Get the PID for the current Window. Atom type; int format; unsigned long nItems; unsigned long bytesAfter; unsigned char *propPID = 0; if (Success == XGetWindowProperty(_display, w, _atomPID, 0, 1, False, XA_CARDINAL, &type, &format, &nItems, &bytesAfter, &propPID)) { if (propPID != 0) { // If the PID matches, add this window to the result set. if (_pid == *((unsigned long *)propPID)) { _result.push_back(w); } XFree(propPID); } } // Recurse into child windows. Window wRoot; Window wParent; Window *wChild; unsigned nChildren; if (0 != XQueryTree(_display, w, &wRoot, &wParent, &wChild, &nChildren)) { for (unsigned i = 0; i < nChildren; i++) { search(wChild[i], _display, _atomPID, _pid); } } // XFree(propPID); }
通过进程id获取窗口winid,一个进程窗口可能有多个子窗口因此会获取到多个winid(不是所有子窗口都是真实窗口),我们取第一个就可以了。
注意:
- 测试发现gtk应用嵌入后输入框无法支持中文输入,Qt应用嵌入后可以正常使用
- 虽然设置了frameless但有些窗口无法生效,可能是现有窗口已经是在frameless窗口上自定义添加的



