关于多进程嵌套,Qt4一直使用QX11Embed,在Qt5被舍弃后,一直无法找到可跨平台的完全的替代品,特别是QX11EmbedWidget::embedInto函数,一直无法找到替代,在这里,实现思路为,依旧使用QProcess,由宿主创建QProcess调用子进程,并且传入宿主WId(这里为了让子进程把parent绑定到宿主上,避免还没绑定上布局就提前show出来了,不美观),在子进程完全启动后,使用IPC把子进程的WId传入到宿主中,宿主收到子进程Wid后,解析为Widget,并放入布局内。在这里,IPC可以有很多中方式,标准输入输出,管道,共享内存,套接字之类的,这里博主选了一个代码量最轻量的方式,QLocalSocket,网络套接字来完成进程间的交互。下面就po出代码吧。
宿主main.cpp
#include "WaitingBarWin.h"
#include <QApplication>
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
WaitingBarWin w;
w.show();
return a.exec();
}
WaitingBarWin.h
#ifndef WAITINGBARWIN_H
#define WAITINGBARWIN_H
#include <QWidget>
#include <QLocalServer>
#include <QLocalSocket>
namespace Ui {
class WaitingBarWin;
}
class QProcess;
class WaitingBarWin : public QWidget
{
Q_OBJECT
public:
explicit WaitingBarWin(QWidget *parent = 0);
~WaitingBarWin();
private:
Ui::WaitingBarWin *ui;
QProcess *m_process;
QLocalServer server;
private slots:
void slot_createWaitingBar();
};
#endif // WAITINGBARWIN_H
WaitingBarWin.cpp
#include "waitingbarwin.h"
#include "ui_waitingbarwin.h"
#include <QProcess>
#include <QDebug>
#include <QWindow>
WaitingBarWin::WaitingBarWin(QWidget *parent) :
QWidget(parent),
ui(new Ui::WaitingBarWin),
m_process(nullptr)
{
ui->setupUi(this);
if(m_process == nullptr)
{
QString cmd = "D:\\Code\\build-SWitchControl-Desktop_Qt_5_12_3_MinGW_64_bit-Debug\\debug\\SWitchControl.exe";//子程序执行文件地址
QStringList argList;
argList << QString::number(this->winId());//把父窗口的id给子进程传递过去
m_process = new QProcess(this);//使用进程运行子进程窗口
QLocalServer::removeServer("SWitchControl");
if(server.listen("SWitchControl"))
{
connect(&server,&QLocalServer::newConnection,[&](){
QLocalSocket* clientConnection = server.nextPendingConnection();
connect(clientConnection,&QLocalSocket::readyRead,this,&WaitingBarWin::slot_createWaitingBar);
});
}
m_process->startDetached(cmd, argList);
}
}
WaitingBarWin::~WaitingBarWin()
{
delete ui;
m_process->terminate();//在父窗口关闭时,主动终止子窗口进程
m_process->waitForFinished(50);
}
void WaitingBarWin::slot_createWaitingBar()
{
QLocalSocket* clientConnection = static_cast<QLocalSocket*>(sender());
QTextStream textStream(clientConnection);
QString wId = textStream.readAll().trimmed();
QWindow *childWin = QWindow::fromWinId(wId.toULongLong());
if(childWin)
{
QWidget *widget = QWidget::createWindowContainer(childWin);//获取一个子进程窗口的widget
ui->verticalLayout->addWidget(widget);//这里是可以使用布局器管理子进程窗口的,不管理的话就在坐标0,0处
}
}
在这里,宿主进程代码就算完成了,还有个WaitingBarWin.ui文件,这里就不贴出了,ui文件里面其实就是放了一个布局控件,叫做verticalLayout。
接下来是子进程的代码
子进程main.cpp
#include <QApplication>
#include <QDebug>
#include <stdio.h>
#include <QLabel>
#include <QWindow>
#include <QLocalSocket>
int main(int argc, char *argv[])
{
if(argc == 2)
{
QApplication a(argc, argv);
//这里是子窗口的顶层窗口
QLabel w;
w.setText("isMe");
//end
WId wid = WId(QString(argv[1]).toInt());//通过参数列表获取父进程窗口的WinId
QWindow *window = QWindow::fromWinId(wid);//获取父进程窗口
w.setProperty("_q_embedded_native_parent_handle", QVariant(wid));//设置属性,这句是必须的
w.winId();//必须调用一次,生成winId
w.windowHandle()->setParent(window);//设置父窗口
w.show();//最后调用show,提前调用qt会为其生成窗口控件,这样就会和你原本想要嵌入进的父进程界面产生冲突
QLocalSocket socket;
socket.connectToServer("SWitchControl");
if(socket.waitForConnected())
{
QString winId = QString::number(w.winId());
QTextStream(&socket)<<winId;
socket.flush();
if(socket.waitForBytesWritten())
{
socket.disconnectFromServer();
}
}
return a.exec();
}
return 0;
}
这里就算全部完成了。不懂可以留言或者评论。