当前位置: 首页 > news >正文

linux桌面软件(wps)内嵌到主窗口后的关闭问题

程序测试环境是:slackware系统,属于linux系统,有桌面(Xface Session)。系统镜像是:slackware64-15.0-install-dvd.iso。qt、c++代码实现。

问题描述:延续上一篇文章,将wps软件窗口内嵌到qt的窗口后,出现一个棘手的问题,就是无法正常的关闭wps进程,即使表面上关闭了,再次打开并内嵌到主窗口时,会出现打不开wps软件的问题,会发现有wps的僵尸进程。

必要条件:slackware系统里需要安装wps、qt5开发工具,本篇文章不做详述。

程序编译:编译还是和上一篇文章保持一致。

我直接说问题的原因,当我们通过fork打开wps软件时,linux系统会出现两个关于该软件的wps进程(wpspdf),通过linux命令查看下进程信息如下:

/office6/wpspdf  这个进程就是打开pdf文件的软件wpspdf进程。

/bin/bash/ 这个进程是wpspdf进程的父进程。qt主窗口创建该进程,该进程又创建打开pdf的进程。

这俩进程都必须正常退出,才能保证我们下一次正确的再次打开软件。

第一种情况:当我们关闭了wps的qt父窗口,而不关闭wps软件界面(比如通过wps右上角的那个关闭按钮)时,虽然wps软件界面没了,但是后台的两个进程都没有关闭,这就导致wps的软件进程处于一种异常状态,这就导致下次打开时,发现完全打不开软件了。

第二种情况:如上图,当我们关闭了wps软件(比如通过wps右上角的那个关闭按钮),也关闭了其qt父窗口,这时我们linux命令查看进程信息会发现,wps进程剩了一个,而且还是个僵尸进程。其实就是/bin/bash/ 那个进程变成了僵尸进程。主要原因就是我们qt主窗口在创建该进程时,我们没有做wait操作,所以该进程没有正常退出,成为了僵尸进程。(上面两张图的进程号不一样的原因是,这是两次测试,所以id值不一样)。

以下是我修改后的最终代码:

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 open_wps();static void open_wps_thread();void close_wps();void close_wps2();void find_window_id_in_tree();void find_window_id_by_class();void add_window_in_qt();private:Ui::Form *ui;QProcess *process;uint32_t window_id;
};#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>
#include <unistd.h>
#include <sys/wait.h>
#include <thread>Form::Form(QWidget *parent) :QWidget(parent),ui(new Ui::Form)
{ui->setupUi(this);
}Form::~Form()
{delete ui;std::cout << "Form deleted" << std::endl;
}void Form::open_wps_thread(){pid_t pid = fork();if(pid == 0){const char* wpsCommand[] = {"wpspdf", nullptr};//wps(word)、et(excel)、wpspdf(pdf)、wpp(ppt)std::cout << "in sub process:" << pid << std::endl;if(execvp(wpsCommand[0], (char* const*)wpsCommand) == -1){perror("failed to exec WPS");}}else if(pid > 0){        std::cout << "in main process:" << pid << std::endl;int status;waitpid(pid, &status, 0);}else{perror("fork failed");}    
}// 打开默认软件
void Form::open_wps(){std::thread t(open_wps_thread);t.detach();
}// 这个函数不太好,只关闭了窗口,没关闭进程
void Form::close_wps(){// 连接到X服务器xcb_connection_t *connection = xcb_connect(NULL, NULL);const xcb_setup_t *setup = xcb_get_setup(connection);xcb_screen_iterator_t iter = xcb_setup_roots_iterator(setup);xcb_screen_t *screen = iter.data;// 创建一个cookie,用于请求服务器关闭窗口xcb_void_cookie_t cookie = xcb_destroy_window_checked(connection, window_id);// 等待请求完成xcb_generic_error_t *error = xcb_request_check(connection, cookie);if (error) {// 处理错误printf("Error code: %d\n", error->error_code);free(error);}// 断开连接xcb_disconnect(connection);
}void Form::close_wps2(){Display *display = XOpenDisplay(NULL);if (!display) {fprintf(stderr, "Cannot open display.\n");return;}    // 创建一个临时窗口来获取默认的事件掩码Window root_return;int x_return, y_return;unsigned int width_return, height_return, border_width_return, depth_return;XGetGeometry(display, window_id, &root_return, &x_return, &y_return,&width_return, &height_return, &border_width_return, &depth_return);// 发送 WM_DELETE_WINDOW 消息XEvent event;memset(&event, 0, sizeof(event));event.type = ClientMessage;event.xclient.display = display;event.xclient.window = window_id;event.xclient.message_type = XInternAtom(display, "WM_PROTOCOLS", False);event.xclient.format = 32;event.xclient.data.l[0] = XInternAtom(display, "WM_DELETE_WINDOW", False);event.xclient.data.l[1] = CurrentTime; // 当前时间戳XSendEvent(display, window_id, False, NoEventMask, &event);XFlush(display);XCloseDisplay(display);
}// 通过系统窗口树形结构去一层层遍历窗口(我这里没有遍历子窗口,如果找不到某个窗口,或许是因为是子窗口)
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,也是窗口的句柄值find_window_id_by_classstd::cout << "Found window name: " << name << " " << win << std::endl;if (std::string(name).find("wpspdf") != std::string::npos) {
//                XFree(name);
//                return;}XFree(name);}// 递归检查子窗口(如果有的话)// 注意:这里为了简化示例,没有实现递归}
}// 将第三方软件(wps)窗口内嵌到qt窗口里
void Form::add_window_in_qt()
{QWindow *win = QWindow::fromWinId(window_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);window_id = win;}}}} else {std::cerr << "Failed to get window list property" << std::endl;}if (data != NULL) {XFree(data);}XCloseDisplay(display);
}

1、重点关注 open_wps_thread线程函数,在该函数里,waitpid(pid, &status, 0)行代码,就是qt主窗口单起个新线程来创建和等待wps的进程,这样该进程就能正常结束了。

2、重点关注close_wps2函数,该函数可以通过窗口id来正常的关闭wps(wpspdf)软件进程。

 mainwindow.cpp

#include "mainwindow.h"
#include "ui_mainwindow.h"
#include "form.h"
#include <unistd.h>MainWindow::MainWindow(QWidget *parent): QMainWindow(parent), ui(new Ui::MainWindow)
{ui->setupUi(this);
}MainWindow::~MainWindow()
{delete ui;
}void MainWindow::on_pushButton_clicked()
{if(!f){f = new Form();}f->show();f->open_wps();sleep(2);f->find_window_id_by_class();
}void MainWindow::on_pushButton_2_clicked()
{if(f){f->close_wps2();}
}//
void MainWindow::on_pushButton_3_clicked()
{if(f){// f->showFullScreen(); // 全屏显示f->add_window_in_qt();// f->find_window_id_in_tree();}
}

 1、mainwindow.cpp也是对应到一个窗口,上面有三个按钮,分别用来调form.h的方法,用来测试的。先调用on_pushButton_clicked,再调用on_pushButton_3_clicked,最后调用on_pushButton_2_clicked就能测试出我们确实正确关闭了窗口。

相关文章:

linux桌面软件(wps)内嵌到主窗口后的关闭问题

程序测试环境是&#xff1a;slackware系统&#xff0c;属于linux系统&#xff0c;有桌面&#xff08;Xface Session&#xff09;。系统镜像是&#xff1a;slackware64-15.0-install-dvd.iso。qt、c代码实现。 问题描述&#xff1a;延续上一篇文章&#xff0c;将wps软件窗口内嵌…...

WindowsTerminal 美化-壁纸随机更换

目录 一. 相关网址二. 壁纸随机更换思路三. 指定 WindowsTermina 壁纸路径四. 编写脚本&#xff0c;随机替换壁纸4.1 powershell脚本4.2 .bat批处理脚本 四. 配置定时任务&#xff0c;添加触发器五. 效果 一. 相关网址 官方下载 Windows Terminal 官方Github微软商店 美化 Oh …...

iOS 多次获取图片主题色不一样

一个需求中&#xff0c;要求获取图片的主题色 代码如下 -(void)kk_getImage:(UIImage *)image fetchthemeColor:(void(^)(UIColor *color))callBack {dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{// 第一步 先把图片缩小 加快计算速度.…...

UE5 武器IK瞄准系统

创建空项目 创建基础蓝图类My_GameMode,My_HUD,My_PlayChar,My_PlayController 项目设置地图模式 近裁平面 0.1 My_PlayChar蓝图中添加摄像机,角色骨骼网格体,武器骨骼网格体 编辑角色骨骼,预览控制器使用特定动画,动画选择ANM_ark-47-Idle hand_r 添加插槽WeaponMes…...

①EtherCAT转ModbusTCP, EtherCAT/Ethernet/IP/Profinet/ModbusTCP协议互转工业串口网关

EtherCAT/Ethernet/IP/Profinet/ModbusTCP协议互转工业串口网关https://item.taobao.com/item.htm?ftt&id822721028899 协议转换通信网关 EtherCAT 转 ModbusTCP GW系列型号 MS-GW15 简介 MS-GW15 是 EtherCAT 和 Modbus TCP 协议转换网关&#xff0c;为用户提供一种 …...

在macOS上进行开发环境配置与应用开发详细的配置指南

在macOS上进行开发环境配置与应用开发&#xff0c;需要遵循一系列步骤来确保你的开发环境既高效又稳定。以下是一个详细的配置指南&#xff0c;涵盖了从安装基本工具到创建应用的整个过程。 1. 安装和更新macOS 首先&#xff0c;确保你的macOS是最新版本。更新系统可以提供更…...

JavaScript 事件处理基础

在网页中添加事件监听器&#xff0c;可以通过JavaScript代码来实现。 要处理用户的交互事件&#xff0c;需要先选择要添加事件监听器的元素&#xff0c;可以使用document.querySelector()或document.getElementById()等方法来获取元素。 然后&#xff0c;使用addEventListene…...

WordPress响应式Git主题响应式CMS主题模板

兼容 IE9、谷歌 Chrome 、火狐 Firefox 等主流浏览器 扁平化的设计加响应式布局&#xff0c;兼容电脑、和各个尺寸手机的完美响应 主题设置面板新增多种AD位&#xff0c;PC端和移动设备各不相同 在主题设置选项中就可以进行基本的SEO设置&#xff1a;首页、分类、文章等页面…...

Solidity 设计模式:实现灵活与可扩展的智能合约架构

Solidity 作为以太坊智能合约的主要编程语言&#xff0c;拥有许多独特的设计模式&#xff0c;这些模式帮助开发者实现更加灵活、可扩展和安全的合约架构。设计模式不仅能够简化开发过程&#xff0c;还能减少常见的编程错误&#xff0c;并提高智能合约的可维护性和可升级性。本文…...

房屋水电费:重新布局,重构JS代码

<!DOCTYPE html> <html lang"zh-CN"> <head><meta charset"UTF-8"><meta name"viewport" content"widthdevice-width, initial-scale1.0"><title>房租水电费</title><script type"…...

Jmeter生成JWT token

JWT简介 JWT官网&#xff1a;https://jwt.io/ JSON Web令牌&#xff08;JWT&#xff09;是一个开放标准&#xff08;RFC 7519&#xff09;&#xff0c;它定义了一种紧凑而自包含的方式&#xff0c;用于在各方之间以JSON对象的形式安全地传输信息。此信息可以验证和信任&#x…...

STM32的ADC技术详解

ADC&#xff08;Analog-to-Digital Converter&#xff0c;模数转换器&#xff09; 是将连续的模拟信号转换为离散的数字信号的关键组件。在STM32系列微控制器中&#xff0c;ADC广泛应用于传感器数据采集、信号处理和控制系统等领域。本文将详细介绍STM32的ADC技术&#xff0c;包…...

PySpark把一列数据上下移动,时序数据

在Pandas中&#xff0c;我们用.shift()把数据框上下移动。 在 PySpark 中&#xff0c;确实存在一个类似于 Pandas 中 shift 函数的功能&#xff0c;它被称为 shiftleft 函数。这个函数用于将给定的值向左移动指定的位数。不过&#xff0c;这与 Pandas 中的 shift 函数有所不同…...

网络基础 【HTTPS】

&#x1f493;博主CSDN主页:麻辣韭菜&#x1f493;   ⏩专栏分类&#xff1a;Linux初窥门径⏪   &#x1f69a;代码仓库:Linux代码练习&#x1f69a; &#x1f4bb;操作环境&#xff1a; CentOS 7.6 华为云远程服务器 &#x1f339;关注我&#x1faf5;带你学习更多Linux知识…...

51单片机的红外感应洗手器【proteus仿真+程序+报告+原理图+演示视频】

1、主要功能 该系统由AT89C51/STC89C52单片机红外感应传感器继电器LED等模块构成。适用于智能红外感应自动洗手器等相似项目。 可实现功能: 1、红外感应传感器实时检测是否有人体接近&#xff08;距离小于20cm&#xff09; 2、如果有人靠近&#xff0c;继电器自动闭合&#…...

【11】纯血鸿蒙HarmonyOS NEXT星河版开发0基础学习笔记-模块化语法与自定义组件

序言&#xff1a; 本文详细讲解了关于鸿蒙系统学习中的模块化语法与自定义组件&#xff0c;在模块化语法中我们学习到了多种导入导出方式&#xff0c;实现了在一个项目中&#xff0c;通过引用不同的组件&#xff0c;让我们整体代码的可读性更强&#xff0c;相当于我们把一个手…...

Angular 客户端渲染时,从 ng-state 里读取 SSR 状态的具体逻辑

Angular 在客户端启动时&#xff0c;如何检测页面中的 ng-state 标签并从中读取 JSON 对象&#xff0c;进而还原应用的状态&#xff0c;是服务器端渲染&#xff08;SSR&#xff09;与客户端渲染&#xff08;CSR&#xff09;无缝衔接的核心环节之一。为了理解这个过程&#xff0…...

C++的联合体union

联合体有点像class类型或者struct类型&#xff0c;只不过它一次只占用一个成员的内存。 通常我们有一个结构体&#xff0c;声明了4个浮点数&#xff0c;那么结构体中就有4*416字节。当我们不断向类或者结构体中添加成员时&#xff0c;其大小也会不断增大。 union只有一个成员…...

JavaScript 中的变量作用域

JavaScript 中的变量作用域 在 JavaScript 中&#xff0c;理解变量作用域是非常重要的&#xff0c;它决定了变量的可见性和生命周期。本文将深入探讨 JavaScript 中的变量作用域&#xff0c;帮助你更好地掌握这一关键概念。 一、什么是变量作用域&#xff1f; 变量作用域指的…...

【C++】二叉搜索树+变身 = 红黑树

&#x1f680;个人主页&#xff1a;小羊 &#x1f680;所属专栏&#xff1a;C 很荣幸您能阅读我的文章&#xff0c;诚请评论指点&#xff0c;欢迎欢迎 ~ 目录 前言一、定义与性质二、红黑树节点的定义三、新增节点插入四、验证红黑树五、AVL树和红黑树比较 前言 本文仅适合了…...

万界星空科技MES数据集成平台

制造执行系统MES作为连接企业上层ERP系统和现场控制系统的桥梁&#xff0c;承担了实时数据采集、处理、分析和传递的重要任务。MES数据集成平台是一个集成各类数据源&#xff0c;将数据进行整合和统一管理的系统&#xff0c;通过提供标准化接口和协议&#xff0c;实现数据的无缝…...

Ajax和axios简单用法

Ajax Ajax&#xff08;Asynchronous JavaScript And XML&#xff0c;异步的JavaScript和XML&#xff09;。 作用是&#xff1a; 数据交换&#xff1a;通过Ajax可以给服务器发送请求&#xff0c;并获取服务器响应的数据。异步交互&#xff1a;可以在不重新加载整个页面的情况…...

Chillax2024.08.01 |免费的白噪音软件

支持多种声音叠加&#xff0c;单独调整音量&#xff0c;定时功能&#xff0c;完全免费。 大小&#xff1a;13.5M 百度网盘&#xff1a;https://pan.baidu.com/s/1dWpdYoO1bPCnHR1bXpTZEg?pwdolxt 夸克网盘&#xff1a;https://pan.quark.cn/s/89dc88c56e26 移动网盘&#xff…...

Python自动化办公:从Excel到PDF生成的全流程

解锁Python编程的无限可能:《奇妙的Python》带你漫游代码世界 在现代办公环境中,数据处理和报表生成是日常工作中非常重要的一环。Python作为一门灵活且功能强大的编程语言,能够通过一系列开源库实现办公自动化。本文将详细讲解如何使用Python实现从Excel数据处理到生成PDF…...

allegro 不同页面相同网路的连接

一、cadence学习笔记&#xff08;1&#xff09;-原理图库制作 绘制好各个界面 放置OFFPAGE 绘制好单个界面是这个样子的&#xff0c;并将剩下的界面进行相同的操作 所有界面完成后&#xff0c;进入设计界面 右键design1.dsn选择Annotate… 点击OK后可以看到WiFi界面OFFPAGE旁边…...

医院管理新趋势:Spring Boot技术引领

4系统概要设计 4.1概述 本系统采用B/S结构(Browser/Server,浏览器/服务器结构)和基于Web服务两种模式&#xff0c;是一个适用于Internet环境下的模型结构。只要用户能连上Internet,便可以在任何时间、任何地点使用。系统工作原理图如图4-1所示&#xff1a; 图4-1系统工作原理…...

Java 新手教程!面向对象设计一口气讲完![]~( ̄▽ ̄)~*(中)

目录 Java 内部类 Java面向对象的设计 - Java 内部类 什么是内部类&#xff1f; 例子 使用内部类的优点 访问局部变量的限制 内部类和继承 内部类中没有静态成员 生成的内部类的类文件 静态上下文中的内类 Java 内部类类型 Java面向对象设计 - Java内部类类型 成员内…...

驰骋低代码功能升级 - 实体功能权限控制

1. 权限控制升级概述 新增功能&#xff1a;对新建、保存、删除、归档、撤销归档等操作的按钮进行精细化的权限控制。展示位置&#xff1a;这些权限控制体现在查询页面和实体卡片页面的工具栏按钮上。 2. 权限控制方式 新建 0. 不控制&#xff1a;任何人都可以新建。1. 指定岗…...

Matlab|考虑阶梯式碳交易机制与电制氢的综合能源系统热电优化

目录 1 主要内容 2 部分程序 3 程序结果 4 下载链接 1 主要内容 该程序复现《考虑阶梯式碳交易机制与电制氢的综合能源系统热电优化》&#xff0c;主要内容&#xff1a;“双碳”背景下&#xff0c;为提高能源利用率&#xff0c;优化设备的运行灵活性&#xff0c;进一步降低…...

Midjourney零基础学习

Midjourney学习笔记TOP01 什么是AI艺术 AI艺术指的是使用AI技术创作的艺术作品&#xff0c;包括AI诗歌、AI音乐、AI绘画等多种艺术表现形式&#xff1b;AI艺术可以被视为计算机程序与人类合作创作作品&#xff1b;除了Midjourney&#xff0c;比较流行的AI图像生成工具还有Stab…...