使用QProcess启动并嵌入带界面应用程序

3 篇文章 0 订阅
1 篇文章 0 订阅

一.概述

    在做系统集成框架软件时,经常遇到需要将已有程序的界面嵌入到统一框架软件的情形,下面主要描述了在Qt5平台下面,使用QProcess启动并嵌入带界面应用程序的方法。该方法在Windows、Linux等操作系统下测试通过,可以实现启动带界面应用程序并嵌入到软件集成框架中的效果。

二.运行效果图

1.Windows平台启动操作系统的记事本程序(Notepad)并将界面嵌入本程序中来

 

2.Linux平台启动操作系统的终端程序(xterm)并将界面嵌入本程序中来

 

三.QProcess嵌入带界面的应用程序的核心方法

QProcess *pProc = new QProcess(this);

pProc->start("Notepad");

pProc->waitForFinished(1000);

WId wid = Pid2Wid(pProc->processId(), "Notepad");//获取窗口标识

QWindow *pWin = QWindow::fromWinId(wid);

QWidget *pWid = QWidget::createWindowContainer(pWinthis);

m_Layout.addWidget(pWid);

四.Windows平台根据进程号、窗口类名获得窗口唯一标识

#include <windows.h>

WId Pid2Wid(quint64 procIDconst char *lpszClassName)

{

char szBuf[256];

HWND hWnd = GetTopWindow(GetDesktopWindow());

while (hWnd)

{

DWORD wndProcID = 0;

GetWindowThreadProcessId(hWnd, &wndProcID);

if (wndProcID == procID)

{

GetClassName(hWndszBufsizeof(szBuf));

if (strcmp(szBuflpszClassName) == 0)

{

return (WId)hWnd;

}

}

hWnd = GetNextWindow(hWndGW_HWNDNEXT);

}

return 0;

}

网上大部分FindWindow查找窗口句柄,该方法还需要应用程序的窗口标题,否则如果已经运行了被启动程序的多个实例,查找的结果不唯一,无法将由QProcess启动的进程被正确嵌入进来。再则窗口的标题会经常变化,比如启动了记事本之后窗口标题一般情况下为“无标题 - 记事本”,只要该文档被保存,窗口的标题的前半部分就变为被保存的文件名称了。

WId wid = (WId)FindWindow("Notepad""无标题 - 记事本");

 

五.Linux平台根据进程号获得窗口唯一标识

#include <X11/Xlib.h>

#include <X11/Xatom.h>

 

class WindowsMatchingPid

{

public:

WindowsMatchingPid(Display *displayWindow wRootunsigned long pid)

_display(display)

_pid(pid)

{

// Get the PID property atom.

_atomPID = XInternAtom(display"_NET_WM_PID"True);

if (_atomPID == None)

{

cout << "No such atom" << endl;

return;

}

 

search(wRoot);

}

 

inline const list<Window> &result() const { return _result; }

 

private:

unsigned long  _pid;

Atom           _atomPID;

Display       *_display;

list<Window>   _result;

 

private:

void search(Window w)

{

// 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(_displayw, &wRoot, &wParent, &wChild, &nChildren))

{

for (unsigned i = 0; i < nChildreni++)

search(wChild[i]);

}

}

};

 

int Pid2Wid(int nPid)

{

// Start with the root window.

char *pDsp = getenv("DISPLAY");

Display *display = XOpenDisplay(pDsp);

if (display == NULL)

{

return 0;

}

 

WindowsMatchingPid match(display, XDefaultRootWindow(display), nPid);

const list<Window> &result = match.result();

if (result.size())

{

return (unsigned long)(*result.begin());

}

else

{

return 0;

}

}

 

六.完整源代码

//widget.h

#ifndef WIDGET_H

#define WIDGET_H

 

#include <QWidget>

#include <QVBoxLayout>

 

namespace Ui {

class Widget;

}

 

class Widget : public QWidget

{

    Q_OBJECT

 

public:

    explicit Widget(QWidget *parent = nullptr);

    ~Widget();

 

private slots:

    void on_pushButton_clicked();

 

private:

    Ui::Widget *ui;

QVBoxLayout m_Layout;

};

 

#endif // WIDGET_H

 

//widget.cpp

#include "widget.h"

#include "ui_widget.h"

#include <iostream>

#include <list>

#include <stdlib.h>

#include <QProcess>

#include <QWindow>

 

using namespace std;

 

#ifdef WIN32

#include <windows.h>

 

WId Pid2Wid(quint64 procIDconst char *lpszClassName)

{

char szBuf[256];

HWND hWnd = GetTopWindow(GetDesktopWindow());

while (hWnd)

{

DWORD wndProcID = 0;

GetWindowThreadProcessId(hWnd, &wndProcID);

if (wndProcID == procID)

{

GetClassName(hWndszBufsizeof(szBuf));

if (strcmp(szBuflpszClassName) == 0)

{

return (WId)hWnd;

}

}

hWnd = GetNextWindow(hWndGW_HWNDNEXT);

}

return 0;

}

 

#else

 

#include <X11/Xlib.h>

#include <X11/Xatom.h>

 

class WindowsMatchingPid

{

public:

WindowsMatchingPid(Display *displayWindow wRootunsigned long pid)

_display(display)

_pid(pid)

{

// Get the PID property atom.

_atomPID = XInternAtom(display"_NET_WM_PID"True);

if (_atomPID == None)

{

cout << "No such atom" << endl;

return;

}

 

search(wRoot);

}

 

inline const list<Window> &result() const { return _result; }

 

private:

unsigned long  _pid;

Atom           _atomPID;

Display       *_display;

list<Window>   _result;

 

private:

void search(Window w)

{

// 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(_displayw, &wRoot, &wParent, &wChild, &nChildren))

{

for (unsigned i = 0; i < nChildreni++)

search(wChild[i]);

}

}

};

 

int Pid2Wid(int nPid)

{

// Start with the root window.

char *pDsp = getenv("DISPLAY");

Display *display = XOpenDisplay(pDsp);

if (display == NULL)

{

return 0;

}

 

WindowsMatchingPid match(display, XDefaultRootWindow(display), nPid);

const list<Window> &result = match.result();

if (result.size())

{

return (unsigned long)(*result.begin());

}

else

{

return 0;

}

}

 

#endif

 

Widget::Widget(QWidget *parent) :

    QWidget(parent),

    ui(new Ui::Widget), m_Layout(this)

{

    ui->setupUi(this);

m_Layout.addWidget(ui->pushButton);

}

 

Widget::~Widget()

{

    delete ui;

}

 

void Widget::on_pushButton_clicked()

{

QProcess *pProc = new QProcess(this);

pProc->setProcessChannelMode(QProcess::ForwardedErrorChannel);

#ifdef WIN32

pProc->start("Notepad");

pProc->waitForFinished(1000);

WId wid = Pid2Wid(pProc->processId(), "Notepad");

#else

pProc->start("xterm");// X11自带的xterm、xclock可以正确地被嵌入启动,xedit等能够被启动,不能得到窗口ID,故不能被正确嵌入。Qt编写的程序应该均能被正确嵌入。

pProc->waitForFinished(1000);

WId wid = Pid2Wid(pProc->processId());

#endif

 

QWindow *pWin = QWindow::fromWinId(wid);

printf("%d\t%p\t%d\n", (int)pProc->processId(), pWin, (int)wid);

if (pWin != NULL)

{

QWidget *pWid = QWidget::createWindowContainer(pWinthis);

m_Layout.addWidget(pWid);

m_Layout.update();

}

}

 

//main.cpp

#include "widget.h"

#include <QApplication>

 

int main(int argcchar *argv[])

{

    QApplication a(argcargv);

    Widget w;

    w.showMaximized();

 

    return a.exec();

}

 

#QProcessEmbedApp.pro

QT       += core gui

greaterThan(QT_MAJOR_VERSION, 4): QT += widgets

TARGET = QProcessEmbedApp

TEMPLATE = app

DEFINES += QT_DEPRECATED_WARNINGS

DEFINES -= UNICODE _UNICODE

CONFIG += c++11

SOURCES += main.cpp widget.cpp

HEADERS += widget.h

FORMS += widget.ui

unix: LIBS += -lX11

 

 

widget.ui

 

<?xml version="1.0" encoding="UTF-8"?>

<ui version="4.0">

 <class>Widget</class>

 <widget class="QWidget" name="Widget">

  <property name="geometry">

   <rect>

    <x>0</x>

    <y>0</y>

    <width>700</width>

    <height>495</height>

   </rect>

  </property>

  <property name="windowTitle">

   <string>QProcessEmbedApp</string>

  </property>

  <widget class="QPushButton" name="pushButton">

   <property name="geometry">

    <rect>

     <x>140</x>

     <y>0</y>

     <width>93</width>

     <height>28</height>

    </rect>

   </property>

   <property name="text">

    <string>StartApp</string>

   </property>

  </widget>

 </widget>

 <layoutdefault spacing="6" margin="11"/>

 <resources/>

 <connections/>

</ui>

 

  • 6
    点赞
  • 69
    收藏
    觉得还不错? 一键收藏
  • 5
    评论
评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值