程序测试环境是:slackware系统,属于linux系统,有桌面(Xface Session)。系统镜像是:slackware64-15.0-install-dvd.iso。qt、c++代码实现。
程序功能:将已经打开的wps(word、pdf等都可以)软件界面内嵌到qt窗口里。
必要条件:slackware系统里需要安装wps、qt5开发工具,本篇文章不做详述。
编译说明:上图是测试demo编译时,在编译工程里加的依赖。 以下截图的部分代码,其他代码不做描述。
form.h:
#ifndef FORM_H
#define FORM_H#include <QWidget>
#include <QProcess>namespace Ui {
class Form;
}class Form : public QWidget
{Q_OBJECTpublic:explicit Form(QWidget *parent = nullptr);~Form();void find_window_id_in_tree();void find_window_id_by_class();void add_window_in_qt(WId id);private:Ui::Form *ui;QProcess *process;
};#endif // FORM_H
form.cpp
#include "form.h"
#include "ui_form.h"
#include <QWindow>
#include <QVBoxLayout>
#include <QtX11Extras/QX11Info>
#include <xcb/xcb.h>
#include <QDebug>
#include <X11/Xlib.h>
#include <X11/Xatom.h>
#include <iostream>
#include <vector>
#include <string>
#include <sstream>
#include <fstream>Form::Form(QWidget *parent) :QWidget(parent),ui(new Ui::Form)
{ui->setupUi(this);find_window_id_in_tree();find_window_id_by_class();
}Form::~Form()
{delete ui;
}// 通过系统窗口树形结构去一层层遍历窗口(我这里没有遍历子窗口,如果找不到某个窗口,或许是因为是子窗口)
void Form::find_window_id_in_tree()
{Display *display = XOpenDisplay(nullptr);if (!display) {std::cerr << "Cannot open display\n";return;}Window root = DefaultRootWindow(display);Window parent, *children;unsigned int num_children;if (!XQueryTree(display, root, &root, &parent, &children, &num_children)) {return;}std::vector<Window> windows;windows.push_back(root);for (unsigned int i = 0; i < num_children; ++i) {windows.push_back(children[i]);}XFree(children);for (Window win : windows) {char* name;int status = XFetchName(display, win, &name);if (status == 1) {// 这里的win就是窗口的id,也是窗口的句柄值std::cout << "Found window name: " << name << " " << win << std::endl;if (std::string(name).find("wps") != std::string::npos) {
// XFree(name);
// return;}XFree(name);}// 递归检查子窗口(如果有的话)// 注意:这里为了简化示例,没有实现递归}
}// 将第三方软件(wps)窗口内嵌到qt窗口里
void Form::add_window_in_qt(WId id)
{QWindow *win = QWindow::fromWinId(id);QWidget *widget = QWidget::createWindowContainer(win);widget->setParent(this);QVBoxLayout *layout = new QVBoxLayout();layout->addWidget(widget);this->setLayout(layout);
}// 获取窗口id(句柄)(找到的另外一种比较快捷的拿到窗口句柄的方式)
void Form::find_window_id_by_class()
{Display* display = XOpenDisplay(NULL);if (!display) {std::cerr << "Failed to open display" << std::endl;return;}Atom netClientListAtom = XInternAtom(display, "_NET_CLIENT_LIST", False);Atom actualType;int format;unsigned long numItems, bytesAfter;unsigned char* data = NULL;int status = XGetWindowProperty(display, DefaultRootWindow(display),netClientListAtom, 0, ~0UL, False,AnyPropertyType,&actualType, &format, &numItems, &bytesAfter,&data);if (status == Success && actualType == XA_WINDOW) {Window* windows = reinterpret_cast<Window*>(data);for (unsigned long i = 0; i < numItems; ++i) {Window win = windows[i];Atom actualType;int format;unsigned long nitems;unsigned long bytes_after;unsigned char* prop_data = nullptr;// 获取WM_CLASS属性if (XGetWindowProperty(display, win, XInternAtom(display, "WM_CLASS", False), 0, 1024, False, XA_STRING,&actualType, &format, &nitems, &bytes_after, &prop_data) == Success) {std::string className(reinterpret_cast<char*>(prop_data));if (actualType == XA_STRING && className == "wpspdf") {std::cout << "Window class name for window " << win << ": " << className << std::endl;XFree(prop_data);add_window_in_qt(win);}}}} else {std::cerr << "Failed to get window list property" << std::endl;}if (data != NULL) {XFree(data);}XCloseDisplay(display);
}
代码需要说明的有如下几点:
1、wps在slackware 中安装好后,可以打开word、pdf、execl、ppt,他们分别对应的可执行文件是wps、wpspdf、et、wpp。
2、本次demo测试,是将pdf窗口内嵌到qt窗口。
3、代码逻辑有些随意,主要是用于测试。效果如下: