程序测试环境是: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_OBJECT
public:
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、代码逻辑有些随意,主要是用于测试。效果如下: