Qt启动嵌入外部进程窗口

Linux x11环境下Qt应用实现多进程窗口嵌入

效果展示

如图:picture

实现说明

启动外部进程,通过进程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 <QDebug>

#include <list>
#include <X11/Xatom.h>
#include <X11/Xlib.h>

static std::list<Window> _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 *>(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窗口上自定义添加的
Logo

为开发者提供学习成长、分享交流、生态实践、资源工具等服务,帮助开发者快速成长。

更多推荐