如何 运行 并在一个 qt qml window 中显示 4 个可执行文件?
How to run and show 4 executables in one qt qml window?
我有 4 个不同的可执行程序,您可以认为它们是相同大小的空矩形 windows,我想 运行 将这些 exe 合并到一个 qt qml window 中。
a,b,c,d 是固定相同大小的不同可执行文件,x 是用 qt5.11/qml quick2 编写的 windows,我如何在 qt/qml 中执行此操作项目,有什么想法吗?
我正在尝试使用 window 容器,但没有任何进展。该 exe 正在将其 window id 写入文本,我正在阅读该文本。
int main(int argc, char *argv[])
{
QApplication app(argc, argv);
QtQuick2ApplicationViewer viewer;
viewer.addImportPath(QLatin1String("modules"));
viewer.setOrientation(QtQuick2ApplicationViewer::ScreenOrientationAuto);
viewer.setMainQmlFile(QLatin1String("qrc:///main.qml"));
viewer.showExpanded();
QProcess ps;
ps.start("sudo ./exe 1");
sleep(10);
ifstream myfile;
myfile.open("winid.txt");
WId id ; myfile >> id;
cout<<"WId ? "<<id<<endl;
myfile.close();
//WId id = (WId)FindWindow(NULL, L"PMON");
QWindow *container = QWindow::fromWinId(id);
container->setFlags(Qt::FramelessWindowHint);
QWidget *program_start = QWidget::createWindowContainer(container);
program_start->setWindowTitle("Fero");
QVBoxLayout *manageWindows = new QVBoxLayout(program_start);
//manageWindows->addWidget(program_start);
//manageWindows->setGeometry(QRect(0,0,1400,800));
program_start->setLayout(manageWindows);
program_start->show();
return app.exec();
}
您基本上是在询问如何创建一个封闭的 windowing 系统。这在某些操作系统中既不简单,也不可能。
如果您的 4 "executables" 是您有权访问的 QML 代码,您可以轻松地将它们组合成一个可执行文件。
如果它们是第 3 方应用程序,那就没那么容易了。在 linux 下,通过使用 wayland 甚至可能使用一些 X API 可以做到这一点。但是在 windows 上你并没有真正获得那种访问权限,至少,我还没有找到这样做的方法,OS 控制进程 windows 并且那里您无能为力。
可能可以使用低级 GUI APIs windows 可能提供,如果可能,隐藏 4 windows 的装饰,并编写 windows 所以它们在您的 QML 应用程序 window 之上,并且随着您的 QML 应用程序 window 缩放和移动,通过代码缩放和移动 4 windows。
无论如何,您似乎大大低估了实现这一点的复杂性,主要是因为人们应该能够做到这一点并不是不合理的期望,但实际情况是不同的。窗口系统在很大程度上仍然是黑盒子,人们不应该干涉这些东西。
假设您确实在尝试将子进程的 GUI 元素嵌入到您自己的进程中,那么您的代码有一些潜在的问题。
首先,在某些平台上 QProcess::start
可能只是将所需数据排队。在进入事件循环之前,子进程实际上不会分叉。因此当你有...
QProcess ps;
ps.start("sudo ./exe 1");
sleep(10);
ifstream myfile;
myfile.open("winid.txt");
WId id;
myfile >> id;
可能 sleep(10)
调用只是阻止了所有内容,并且当您尝试阅读时该过程尚未开始。即使子进程确实启动了,也不能保证它会在您阅读它时将其 window id 写入 winid.txt
—— 最好对 QProcess::readyReadStandardOutput
信号采取行动。
其次,您将完整的命令行传递给QProcess::start
。在某些将通过 shell 传递命令的平台上,这意味着您必须非常小心 quoting/escaping 特殊字符。最好使用...
void QProcess::start(const QString &program, const QStringList &arguments, QIODevice::OpenMode mode = ReadWrite)
...改为过载。
最后,您实际上 运行 在这里使用的命令是 sudo
。假设这是在 Linux
系统(或类似系统)上,那么 sudo
可能需要密码,而您还没有设置任何提供密码的方法。
举例来说,以下代码根据其调用方式执行以下两种操作之一。
如果使用单个命令行参数调用它 creates/shows 一个 QPushButton
派生小部件并将该小部件的 window id 写入标准输出。
如果调用时不带参数,它将充当父进程。它启动几个子进程,当每个子进程将其 window id 打印到标准输出时,捕获关联的小部件并将其嵌入到自己的子进程中。
#include <cstdlib>
#include <iostream>
#include <set>
#include <QApplication>
#include <QDebug>
#include <QLabel>
#include <QProcess>
#include <QPushButton>
#include <QVBoxLayout>
#include <QWidget>
#include <QWindow>
namespace {
/*
* This is the basic QPushButton derived widget that will be created by the
* child process(es).
*/
class remote_process_widget: public QPushButton {
using super = QPushButton;
public:
explicit remote_process_widget (const QString &name, QWidget *parent = nullptr)
: super(name, parent)
{
}
};
}
int
main (int argc, char **argv)
{
try {
QApplication app(argc, argv);
std::set<QProcess *> processes;
if (argc > 1) {
/*
* This process was started with at least one command line arg so we
* assume it's a managed child process. Need to write the window id to
* stdout for the parent process to read.
*/
auto *w = new remote_process_widget(QString::fromStdString(argv[1]));
w->show();
std::cout << w->winId() << std::endl;
} else {
/*
* No command line args so start up as the parent process. Create some
* child processes and set things up to manage their widgets.
*/
auto *w = new QWidget;
auto *l = new QVBoxLayout(w);
auto *label = new QLabel("Parent process");
label->setAlignment(Qt::AlignCenter);
l->addWidget(label);
w->show();
/*
* Now create/start the child processes.
*/
for (int i = 0; i < 4; ++i) {
auto *process = new QProcess;
processes.insert(process);
/*
* Connect to the `QProcess::readyReadStandardOutput` signal of the
* child. This is where the real work is done regarding the
* capture/embedding of the child processes widgets.
*/
QObject::connect(process, &QProcess::readyReadStandardOutput,
[l, process]()
{
auto wid = QString(process->readAllStandardOutput()).toULongLong();
std::cout << "wid = " << wid << "\n";
if (auto *window = QWindow::fromWinId(wid)) {
if (auto *container = QWidget::createWindowContainer(window)) {
l->addWidget(container);
}
}
});
/*
* Start the child process.
*/
process->start(argv[0], QStringList() << QString("Remote process %1").arg(i));
}
}
app.exec();
/*
* Shut down all child processes.
*/
for (auto process: processes) {
process->terminate();
std::cout << "waiting for process " << process->processId() << " to terminate\n";
while (!process->waitForFinished())
;
}
std::cout << "done\n";
}
catch (std::exception &ex) {
qCritical() << "\n" << ex.what();
}
catch (...) {
qCritical() << "\nunrecognized exception";
}
exit(0);
}
因此,虽然它不使用 QML
,但如果您 运行 它没有任何参数,它应该创建自己的小部件,创建四个子进程并嵌入与这些子进程关联的小部件.像...
如果您在 linux,您可以编写一个 wayland 合成器来编写您的应用程序。
这应该可以满足您的要求:
import QtQuick 2.0
import QtWayland.Compositor 1.3 // or 1.2 on Qt 5.11
import QtQuick.Window 2.2
WaylandCompositor {
id: wlcompositor
WaylandOutput {
sizeFollowsWindow: true
compositor: wlcompositor
window: Window {
width: 1024
height: 768
visible: true
title: wlcompositor.socketName
Grid {
columns: 2
Repeater {
model: shellSurfaces
ShellSurfaceItem {
autoCreatePopupItems: true
shellSurface: modelData
onSurfaceDestroyed: shellSurfaces.remove(index)
}
}
}
}
}
ListModel { id: shellSurfaces }
// Qt 5.11+
XdgShellV6 {
onToplevelCreated: shellSurfaces.append({shellSurface: xdgSurface})
}
// Qt 5.12+
XdgShell {
onToplevelCreated: shellSurfaces.append({shellSurface: xdgSurface})
}
// Qt 5.12+ Disable window decorations (for qt applications you can also do this by setting
// QT_WAYLAND_DISABLE_WINDOWDECORATION=1 in the client's environment (any version).
XdgDecorationManagerV1 {
preferredMode: XdgToplevel.ServerSideDecoration
}
}
然后可以使用 ./myclient -platform wayland
启动客户端。
如果您是 运行 嵌套的 Wayland 会话,则必须通过相应地设置 WAYLAND_DISPLAY
来指定它们应该连接到内部合成器,即 env WAYLAND_DISPLAY=wayland-1 ./myclient -platform wayland
经过长时间的研究,我本可以在 qt 应用程序中 运行 一个可执行文件,我就是这样做的:如果你的程序有一个 window 每个 windows
有一个 id,你可以使用它,首先我 运行 宁我的外包可执行文件(4 spearate 进程,但例如相同的 exe)他们正在将他们的 win id 写入一个文件,然后是 exes write finished, my main window program reading that wid
s 并使用该 wid`s 创建 qt 容器(wid 具有一些功能,如 x 和 y 轴)当 qt 容器创建时,每个进程都进入该 qt 容器,现在你有一些单独的进程 运行ning 在一个分裂的内部window。
enter image description here
int main(int argc, char *argv[]){
QApplication app(argc, argv);
QtQuick2ApplicationViewer viewer;
viewer.addImportPath(QLatin1String("modules"));
viewer.setOrientation(QtQuick2ApplicationViewer::ScreenOrientationAuto);
viewer.setMainQmlFile(QLatin1String("qrc:///main.qml"));
viewer.showExpanded();
QProcess ps;
ps.start("sudo ./exe 1");
sleep(10);
ifstream myfile;
myfile.open("winid.txt");
WId id ; myfile >> id;
cout<<"WId ? "<<id<<endl;
myfile.close();
QTableWidget* grids ;
CreateContainer createContainerOBJ;
grids->setCellWidget(i+(i+1),j,createContainerOBJ.createContainer(id[i*tableCol+j]));//createConteiner 是一个函数,下面有两行
//createContainer函数内容
//QWindow *container = QWindow::fromWinId(id);
//program_start = QWidget::createWindowContainer(容器);
//manageWindows->addWidget(program_start);
//manageWindows->setGeometry(QRect(0,0,1400,800));
program_start->setLayout(manageWindows);
program_start->show();
return app.exec();}
我有 4 个不同的可执行程序,您可以认为它们是相同大小的空矩形 windows,我想 运行 将这些 exe 合并到一个 qt qml window 中。
a,b,c,d 是固定相同大小的不同可执行文件,x 是用 qt5.11/qml quick2 编写的 windows,我如何在 qt/qml 中执行此操作项目,有什么想法吗?
我正在尝试使用 window 容器,但没有任何进展。该 exe 正在将其 window id 写入文本,我正在阅读该文本。
int main(int argc, char *argv[])
{
QApplication app(argc, argv);
QtQuick2ApplicationViewer viewer;
viewer.addImportPath(QLatin1String("modules"));
viewer.setOrientation(QtQuick2ApplicationViewer::ScreenOrientationAuto);
viewer.setMainQmlFile(QLatin1String("qrc:///main.qml"));
viewer.showExpanded();
QProcess ps;
ps.start("sudo ./exe 1");
sleep(10);
ifstream myfile;
myfile.open("winid.txt");
WId id ; myfile >> id;
cout<<"WId ? "<<id<<endl;
myfile.close();
//WId id = (WId)FindWindow(NULL, L"PMON");
QWindow *container = QWindow::fromWinId(id);
container->setFlags(Qt::FramelessWindowHint);
QWidget *program_start = QWidget::createWindowContainer(container);
program_start->setWindowTitle("Fero");
QVBoxLayout *manageWindows = new QVBoxLayout(program_start);
//manageWindows->addWidget(program_start);
//manageWindows->setGeometry(QRect(0,0,1400,800));
program_start->setLayout(manageWindows);
program_start->show();
return app.exec();
}
您基本上是在询问如何创建一个封闭的 windowing 系统。这在某些操作系统中既不简单,也不可能。
如果您的 4 "executables" 是您有权访问的 QML 代码,您可以轻松地将它们组合成一个可执行文件。
如果它们是第 3 方应用程序,那就没那么容易了。在 linux 下,通过使用 wayland 甚至可能使用一些 X API 可以做到这一点。但是在 windows 上你并没有真正获得那种访问权限,至少,我还没有找到这样做的方法,OS 控制进程 windows 并且那里您无能为力。
可能可以使用低级 GUI APIs windows 可能提供,如果可能,隐藏 4 windows 的装饰,并编写 windows 所以它们在您的 QML 应用程序 window 之上,并且随着您的 QML 应用程序 window 缩放和移动,通过代码缩放和移动 4 windows。
无论如何,您似乎大大低估了实现这一点的复杂性,主要是因为人们应该能够做到这一点并不是不合理的期望,但实际情况是不同的。窗口系统在很大程度上仍然是黑盒子,人们不应该干涉这些东西。
假设您确实在尝试将子进程的 GUI 元素嵌入到您自己的进程中,那么您的代码有一些潜在的问题。
首先,在某些平台上 QProcess::start
可能只是将所需数据排队。在进入事件循环之前,子进程实际上不会分叉。因此当你有...
QProcess ps;
ps.start("sudo ./exe 1");
sleep(10);
ifstream myfile;
myfile.open("winid.txt");
WId id;
myfile >> id;
可能 sleep(10)
调用只是阻止了所有内容,并且当您尝试阅读时该过程尚未开始。即使子进程确实启动了,也不能保证它会在您阅读它时将其 window id 写入 winid.txt
—— 最好对 QProcess::readyReadStandardOutput
信号采取行动。
其次,您将完整的命令行传递给QProcess::start
。在某些将通过 shell 传递命令的平台上,这意味着您必须非常小心 quoting/escaping 特殊字符。最好使用...
void QProcess::start(const QString &program, const QStringList &arguments, QIODevice::OpenMode mode = ReadWrite)
...改为过载。
最后,您实际上 运行 在这里使用的命令是 sudo
。假设这是在 Linux
系统(或类似系统)上,那么 sudo
可能需要密码,而您还没有设置任何提供密码的方法。
举例来说,以下代码根据其调用方式执行以下两种操作之一。
如果使用单个命令行参数调用它 creates/shows 一个 QPushButton
派生小部件并将该小部件的 window id 写入标准输出。
如果调用时不带参数,它将充当父进程。它启动几个子进程,当每个子进程将其 window id 打印到标准输出时,捕获关联的小部件并将其嵌入到自己的子进程中。
#include <cstdlib>
#include <iostream>
#include <set>
#include <QApplication>
#include <QDebug>
#include <QLabel>
#include <QProcess>
#include <QPushButton>
#include <QVBoxLayout>
#include <QWidget>
#include <QWindow>
namespace {
/*
* This is the basic QPushButton derived widget that will be created by the
* child process(es).
*/
class remote_process_widget: public QPushButton {
using super = QPushButton;
public:
explicit remote_process_widget (const QString &name, QWidget *parent = nullptr)
: super(name, parent)
{
}
};
}
int
main (int argc, char **argv)
{
try {
QApplication app(argc, argv);
std::set<QProcess *> processes;
if (argc > 1) {
/*
* This process was started with at least one command line arg so we
* assume it's a managed child process. Need to write the window id to
* stdout for the parent process to read.
*/
auto *w = new remote_process_widget(QString::fromStdString(argv[1]));
w->show();
std::cout << w->winId() << std::endl;
} else {
/*
* No command line args so start up as the parent process. Create some
* child processes and set things up to manage their widgets.
*/
auto *w = new QWidget;
auto *l = new QVBoxLayout(w);
auto *label = new QLabel("Parent process");
label->setAlignment(Qt::AlignCenter);
l->addWidget(label);
w->show();
/*
* Now create/start the child processes.
*/
for (int i = 0; i < 4; ++i) {
auto *process = new QProcess;
processes.insert(process);
/*
* Connect to the `QProcess::readyReadStandardOutput` signal of the
* child. This is where the real work is done regarding the
* capture/embedding of the child processes widgets.
*/
QObject::connect(process, &QProcess::readyReadStandardOutput,
[l, process]()
{
auto wid = QString(process->readAllStandardOutput()).toULongLong();
std::cout << "wid = " << wid << "\n";
if (auto *window = QWindow::fromWinId(wid)) {
if (auto *container = QWidget::createWindowContainer(window)) {
l->addWidget(container);
}
}
});
/*
* Start the child process.
*/
process->start(argv[0], QStringList() << QString("Remote process %1").arg(i));
}
}
app.exec();
/*
* Shut down all child processes.
*/
for (auto process: processes) {
process->terminate();
std::cout << "waiting for process " << process->processId() << " to terminate\n";
while (!process->waitForFinished())
;
}
std::cout << "done\n";
}
catch (std::exception &ex) {
qCritical() << "\n" << ex.what();
}
catch (...) {
qCritical() << "\nunrecognized exception";
}
exit(0);
}
因此,虽然它不使用 QML
,但如果您 运行 它没有任何参数,它应该创建自己的小部件,创建四个子进程并嵌入与这些子进程关联的小部件.像...
如果您在 linux,您可以编写一个 wayland 合成器来编写您的应用程序。
这应该可以满足您的要求:
import QtQuick 2.0
import QtWayland.Compositor 1.3 // or 1.2 on Qt 5.11
import QtQuick.Window 2.2
WaylandCompositor {
id: wlcompositor
WaylandOutput {
sizeFollowsWindow: true
compositor: wlcompositor
window: Window {
width: 1024
height: 768
visible: true
title: wlcompositor.socketName
Grid {
columns: 2
Repeater {
model: shellSurfaces
ShellSurfaceItem {
autoCreatePopupItems: true
shellSurface: modelData
onSurfaceDestroyed: shellSurfaces.remove(index)
}
}
}
}
}
ListModel { id: shellSurfaces }
// Qt 5.11+
XdgShellV6 {
onToplevelCreated: shellSurfaces.append({shellSurface: xdgSurface})
}
// Qt 5.12+
XdgShell {
onToplevelCreated: shellSurfaces.append({shellSurface: xdgSurface})
}
// Qt 5.12+ Disable window decorations (for qt applications you can also do this by setting
// QT_WAYLAND_DISABLE_WINDOWDECORATION=1 in the client's environment (any version).
XdgDecorationManagerV1 {
preferredMode: XdgToplevel.ServerSideDecoration
}
}
然后可以使用 ./myclient -platform wayland
启动客户端。
如果您是 运行 嵌套的 Wayland 会话,则必须通过相应地设置 WAYLAND_DISPLAY
来指定它们应该连接到内部合成器,即 env WAYLAND_DISPLAY=wayland-1 ./myclient -platform wayland
经过长时间的研究,我本可以在 qt 应用程序中 运行 一个可执行文件,我就是这样做的:如果你的程序有一个 window 每个 windows
有一个 id,你可以使用它,首先我 运行 宁我的外包可执行文件(4 spearate 进程,但例如相同的 exe)他们正在将他们的 win id 写入一个文件,然后是 exes write finished, my main window program reading that wid
s 并使用该 wid`s 创建 qt 容器(wid 具有一些功能,如 x 和 y 轴)当 qt 容器创建时,每个进程都进入该 qt 容器,现在你有一些单独的进程 运行ning 在一个分裂的内部window。
enter image description here
int main(int argc, char *argv[]){
QApplication app(argc, argv);
QtQuick2ApplicationViewer viewer;
viewer.addImportPath(QLatin1String("modules"));
viewer.setOrientation(QtQuick2ApplicationViewer::ScreenOrientationAuto);
viewer.setMainQmlFile(QLatin1String("qrc:///main.qml"));
viewer.showExpanded();
QProcess ps;
ps.start("sudo ./exe 1");
sleep(10);
ifstream myfile;
myfile.open("winid.txt");
WId id ; myfile >> id;
cout<<"WId ? "<<id<<endl;
myfile.close();
QTableWidget* grids ;
CreateContainer createContainerOBJ;
grids->setCellWidget(i+(i+1),j,createContainerOBJ.createContainer(id[i*tableCol+j]));//createConteiner 是一个函数,下面有两行
//createContainer函数内容
//QWindow *container = QWindow::fromWinId(id);
//program_start = QWidget::createWindowContainer(容器);
//manageWindows->addWidget(program_start);
//manageWindows->setGeometry(QRect(0,0,1400,800));
program_start->setLayout(manageWindows);
program_start->show();
return app.exec();}